375
function sayName(params: {firstName: string; lastName?: string}) {
    params.lastName = params.lastName || 'smith';  // <<-- any better alternative to this?
    var name = params.firstName + params.lastName
    alert(name);
}

sayName({firstName: 'bob'});

I had imagined something like this might work:

function sayName(params: {firstName: string; lastName: string = 'smith'}) {

Obviously if these were plain arguments you could do it with:

function sayName(firstName: string, lastName = 'smith') {
    var name = firstName + lastName;
    alert(name);
}

sayName('bob');

And in coffeescript you have access to the conditional existence operator so can do:

param.lastName ?= 'smith'

Which compiles to the javascript:

if (param.lastName == null) {
    param.lastName = 'smith';
}
frederj
  • 1,483
  • 9
  • 20
AJP
  • 26,547
  • 23
  • 88
  • 127
  • Your suggested solution `params.lastName = params.lastName || 'smith';` is actually rather fine - it handles empty strings, undefined strings and null values. – Fenton Apr 26 '14 at 19:25
  • @SteveFenton And anything falsy. It's fine in the case of a last name, but generally not a good idea. That's why Typescript translates default values to `if(typeof x === "undefined") { … }`. Not that you don't know that, but just pointing out the general case for the OP. – Ingo Bürk Apr 26 '14 at 20:06
  • 1
    @IngoBürk true, but due to `lastName?: string` it can only ever be as SteveFenton said "handles empty strings, undefined strings and null values". – AJP Apr 26 '14 at 20:37
  • @AJP Within Typescript, yes. But that's just compile-time. Either way, like I said, just pointing out a general case scenario :) – Ingo Bürk Apr 26 '14 at 20:52
  • 2
    Agree with steve here. Quite commonly it is better to have a single config argument instead of e.g. 10 arguments. In this case `params.lastName = params.lastName || 'smith';` is the pattern I use – basarat Apr 27 '14 at 07:14
  • 1
    https://www.typescripttutorial.net/typescript-tutorial/typescript-default-parameters/ `param1: type = defaultValue` – Germa Vinsmoke Jan 26 '21 at 10:46

8 Answers8

418

Actually, there appears to now be a simple way. The following code works in TypeScript 1.5:

function sayName({ first, last = 'Smith' }: {first: string; last?: string }): void {
  const name = first + ' ' + last;
  console.log(name);
}

sayName({ first: 'Bob' });

The trick is to first put in brackets what keys you want to pick from the argument object, with key=value for any defaults. Follow that with the : and a type declaration.

This is a little different than what you were trying to do, because instead of having an intact params object, you have instead have dereferenced variables.

If you want to make it optional to pass anything to the function, add a ? for all keys in the type, and add a default of ={} after the type declaration:

function sayName({first='Bob',last='Smith'}: {first?: string; last?: string}={}){
    var name = first + " " + last;
    alert(name);
}

sayName();
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
jpadvo
  • 6,031
  • 3
  • 26
  • 30
  • That solution makes for an *interetsting* code-hinting/intellisense feature as it shows the parameters fully destructured. I wouldn't expect to see that style in a settings object with more than a few parameters like in the example. – WiredPrairie Oct 19 '15 at 12:09
  • 8
    Is is also possible to rewrite this solution to use a interface/class like "sayNameSettings" instead? – Stef Heyenrath Mar 27 '17 at 07:12
  • 24
    While this works, destructuring the arguments this way makes it hard to follow IMO. – PW Kad Jun 12 '17 at 23:14
  • 1
    you can use 'as' keyword (in my opinion it's more readable), like {first='Bob',last='Smith'} as {first?: string; last?: string} but ideally you should create an interface for handle this cases... – Frohlich Nov 29 '18 at 12:30
  • 1
    You can also use the Partial utility types to the type part in the argument, if you have a pre-defined interface/type for the argument – Xin Chen Oct 11 '19 at 22:59
  • 3
    Is there a way to pass the options object to another function? – Dylanthepiguy Mar 24 '20 at 19:46
  • 1
    I think it's much easier & more readable to define the type: `export type NameBits = { first?: string; last?: string; };` You can determine what's optional by deciding what to give a default value in the assignment. The optional flags `?` in the type def make it passable to declare an object missing those properties **of that type**. This way you can pass the argument object by name. `function sayName( nameBits: NameBits = { first: "Bob", Last: "Smith" } ) { /* Destructure named vars if you want */ const { first, last } = nameBits; alert(\`${first} ${last}\` ); }` – Rik Oct 05 '20 at 05:34
  • 1
    Unfortunately using `function sayName( nameBits: NameBits = { first: "Bob", Last: "Smith" } )` and calling it with `sayName({ first: "Alice" })` results in `nameBits` being `{ first: "Alice" }`. i.e. `Last` is not set to the default value of `Smith` – flux Apr 17 '21 at 13:28
  • Why you need a `={}` when you already have `first='Bob'` and `last='Smith'`? – NeoZoom.lua Nov 23 '21 at 08:54
  • 3
    @Rainning It's kind of tricky, but that's the default value of the whole parameter object. It allows you to call the function without providing *any* arguments. Because that's there, you could call this function like so: `sayName()`. If you remove the `={}`, you'd have to at least pass in an empty object. – Daniel Kaplan Jan 10 '22 at 04:13
  • @DanielKaplan: Thanks for the explanation. Wow but I still think that requiring passing a `{}` is kind of an ugly design. – NeoZoom.lua Jan 10 '22 at 05:50
  • 1
    The readability of this sucks. – JCraine Aug 22 '22 at 23:17
83

Typescript supports default parameters now:

https://www.typescriptlang.org/docs/handbook/functions.html

Also, adding a default value allows you to omit the type declaration, because it can be inferred from the default value:

function sayName(firstName: string, lastName = "Smith") {
  const name = firstName + ' ' + lastName;
  alert(name);
}

sayName('Bob');
encrest
  • 1,757
  • 1
  • 17
  • 18
  • 74
    How does this apply to the OP's question regarding default value for TypeScript object passed as an argument? You should show an example with an object argument. – demisx Jan 26 '18 at 23:56
  • 18
    It doesn't, but I found this question when I was searching for how to provide default parameters in typescript in general, and none of the answers addressed that. I also posted this response before I noticed the subtle "object" param clarification. – encrest Jan 30 '18 at 18:30
  • this doesn't apply to the question as this is already mentioned in there. OP is asking for default value in JSON – TalESid Feb 07 '22 at 09:40
  • this should be top answer. thank you. – vikingsteve Apr 30 '23 at 10:51
76

Object destructuring the parameter object is what many of the answers above are aiming for and Typescript now has the methods in place to make it much easier to read and intuitively understand.

Destructuring Basics: By destructuring an object, you can choose properties from an object by key name. You can define as few or as many of the properties you like, and default values are set by a basic syntax of let {key = default} = object.

let {firstName, lastName = 'Smith'} = myParamsObject;

//Compiles to:
var firstName = myParamsObject.firstName, 
_a = myParamsObject.lastName, 
lastName = _a === void 0 ? 'Smith' : _a;

Writing an interface, type or class for the parameter object improves legibility.

type FullName = {
  firstName: string;
   
  /** @defaultValue 'Smith' */
  lastName ? : string;
}

function sayName(params: FullName) {

  // Set defaults for parameter object
  var { firstName, lastName = 'Smith'} = params;

  // Do Stuff
  var name = firstName + " " + lastName;
  alert(name);
}

// Use it
sayName({
  firstName: 'Bob'
});
Francis Upton IV
  • 19,322
  • 3
  • 53
  • 57
Benson
  • 4,181
  • 2
  • 26
  • 44
  • 3
    This is so much better than the accepted answer. Destruction inside the function body makes more sense than in the paramter itself – DollarAkshay Aug 25 '21 at 12:07
  • 5
    @DollarAkshay: why does it make more sense to hide in the function body what the default value of a function _parameter_ is? – Dan Dascalescu Apr 17 '22 at 19:15
  • Just an FYI as I came across this, if you're wanting to have all of the properties as optional, you must provide a default blank object i.e. `function sayName(params: FullName = {})` – Emobe Dec 20 '22 at 21:55
  • Yes Emobe, though some versions of TypeScript may complain about type of `{}` You can fix this by adding a `as FullName` typecast to your empty object like this: `function sayName(params: FullName = {} as Fullname)` – Benson Jan 26 '23 at 05:39
30

No, TypeScript doesn't have a natural way of setting defaults for properties of an object defined like that where one has a default and the other does not. You could define a richer structure:

class Name {
    constructor(public first : string, 
        public last: string = "Smith") {

    }
}

And use that in place of the inline type definition.

function sayName(name: Name) {
    alert(name.first + " " + name.last);
}

You can't do something like this unfortunately:

function sayName(name : { first: string; last?:string } 
       /* and then assign a default object matching the signature */  
       = { first: null, last: 'Smith' }) {

} 

As it would only set the default if name was undefined.

WiredPrairie
  • 58,954
  • 17
  • 116
  • 143
26

This can be a nice way to do it that does not involve long constructors

class Person {
    firstName?: string = 'Bob';
    lastName?: string = 'Smith';

    // Pass in this class as the required params
    constructor(params: Person) {
        // object.assign will overwrite defaults if params exist
        Object.assign(this, params)
    }
}

// you can still use the typing 
function sayName(params: Person){ 
    let name = params.firstName + params.lastName
    alert(name)
}

// you do have to call new but for my use case this felt better
sayName(new Person({firstName: 'Gordon'}))
sayName(new Person({lastName: 'Thomas'}))
Cameron
  • 826
  • 10
  • 10
  • 2
    @PWKad the defaults are set on the second and third line, "Bob" and "Smith" – Cameron Feb 20 '18 at 09:37
  • 1
    I know this is an old answer, but a possible problem with this solution is that you can pass these arguments with `null` or `undefined` value because the `Person` class allows that. So it's syntactically valid to write `sayName(new Person({firstName: undefined }));` while it might not make any sense semantically. – csisy Dec 09 '21 at 09:43
  • @csisy Could you elaborate on why this might be an issue? I'm pretty new to all this, and this solution seems the nicest option to me, insofar as it allows one to set defaults for the individual keys, as well as a default for the `params` object, itself; however, I want to be sure I understand the possible consequences of going this direction. Any insight would be appreciated. – Rax Adaam Aug 25 '22 at 16:50
5

Here is something to try, using interface and destructuring with default values. Please note that "lastName" is optional.

interface IName {
  firstName: string
  lastName?: string
}

function sayName(params: IName) {
  const { firstName, lastName = "Smith" } = params
  const fullName = `${firstName} ${lastName}`

  console.log("FullName-> ", fullName)
}

sayName({ firstName: "Bob" })
Ben Dev
  • 835
  • 1
  • 10
  • 10
1

Without destructuring, you can create a defaults params and pass it in

interface Name {
   firstName: string;
   lastName: string;
}

export const defaultName extends Omit<Name, 'firstName'> {
    lastName: 'Smith'
}

sayName({ ...defaultName, firstName: 'Bob' })
A F
  • 7,424
  • 8
  • 40
  • 52
1

There is another way without destructuring could be to set the default value with the type like this:

function name(param1:type=defaultValue1, paramr2:type=defaultvalue2,...) {//}

An example could be

function addTwoNumbers(first:number = 1, second:number = 3):number {return first+second}
console.log(addTwoNumbers())
Ankur Sehdev
  • 106
  • 1
  • 1
  • 8
  • Yep! However the benefit of having a function with a single argument that is an object is that the implementation here would not be able to type check if you give the arguments in the wrong order. For example: `function divideTwoNumbers(numerator: number=1, denominator: number=2) { return numerator / denominator }` would give the wrong result of `5` instead of `0.1` but not raise a type error if you invoked it like: `const denominator = 10; divideTwoNumbers(denominator)`. Secondly to invoke it and use the default is not elegant/easy: `divideTwoNumbers(undefined, denominator)` – AJP Oct 25 '22 at 14:33