74

I have problems when using named parameters in TypeScript. I know it is not supported the way I use it in TypeScript.

But how can I do it?

TypeScript:

SomeFunction(name1: boolean, 
             name2: boolean, 
             name3: boolean, 
             name4: boolean) // Will occur only one time, 
                            // so the change should be in TypeScript

JavaScript:

$(function () {
       ...SomeFunction({name1:false, 
                        name2:false, 
                        name3:false, 
                        name4:true}); // Will occur 100 times
});

I was looking at (this did not work out):

Is there a way to provide named parameters in a function call in JavaScript?

How can I add optional named parameters to a TypeScript function parameter?

What can I do in TypeScript, to use named parameters in JavaScript?

What I wonder is, that Visual Studio 2015 did not show a syntax error when using named parameter the way I used it in TypeScript...

PS.: I use TypeScript 2.1

5
  • 4
    Well, call-time named parameters don't exist in either TS nor JS. Why didn't the other solutions you link to work for you? They should. Commented Feb 8, 2017 at 9:10
  • @deceze stackoverflow.com/questions/11796093/… the post by Ray Perea (actually an JS-Object) Commented Feb 8, 2017 at 9:26
  • And how does that not work for you…? Commented Feb 8, 2017 at 9:27
  • Stepping into typescript the object will look strange, take a look pasteboard.co/vNZ5OEg16.png (similar example) Commented Feb 8, 2017 at 9:36
  • 1
    Passing an object is not the same as named parameters. An object can be modified (ie is somekind ref parameter. A parameter can be localy modified but that change is does not seen at return time; it is a local parameter. Commented Mar 6, 2018 at 14:15

7 Answers 7

85

True named parameters don't exist in JavaScript nor in TypeScript but you can use destructuring to simulate named parameters:

interface Names {
    name1: boolean
    name2: boolean
    name3: boolean
    name4: boolean
}

function myFunction({name1, name2, name3, name4}: Names) {
    // name1, etc. are boolean
}

Notice: The type Names is actually optional. The following JavaScript code (without typing) is valid in TS:

function myFunction({name1, name2, name3, name4}) {
    // name1, etc. are of type any
}
Sign up to request clarification or add additional context in comments.

3 Comments

The last part, while being right, it's not truly Typescript, it being the main issue here, not javascript.
How do I access them in the function? Compiler keeps telling me they are undefined.
Although pragmatically correct answer, the question and answers make the same naming mistake. Parameters are named in JS (and any other language) by default. The author asks about argument labels.
26

The only way to get something close to "named parameters" is to use a single object parameter:

type SomeFunctionParams = {
    name1: boolean;
    name2: boolean;
    name3: boolean;
    name4: boolean;
}

SomeFunction(params: SomeFunctionParams) { ... }

And then:

$(function () {
    SomeFunction({
        name1:false,
        name2:false,
        name3:false,
        name4:true
    });
});

Comments

9

Anonymous interface

I just wanted to note that you can also declare the types without a name inside the argument list as follows, which saves us from typing interface myfuncArgs.

It still forces me to retype every single parameter which is a pain, but I don't know how to avoid that for now.

const assert = require('assert')

function myfunc({
    myInt,
    myString,
  }: {
    myInt: number,
    myString?: string,
  }
) {
  return `${myInt} ${myString}`
}

assert.strictEqual(
  myfunc({
    myInt: 1,
    myString: 'abc',
  }),
  '1 abc'
)

assert.strictEqual(
  myfunc({
    myInt: 1,
  }),
  '1 undefined'
)

// Fails as desired since myInt is not optional and was not given.
//assert.strictEqual(
//  myfunc({
//    myString: 'abc',
//  }),
//  'unefined abc'
//)

// Fails as desired since wrong type of myInt.
//assert.strictEqual(
//  myfunc({
//    myInt: '1',
//    myString: 'abc',
//  }),
//  '1 abc'
//)

Compile and run:

npx tsc main.ts
node main.js

Tested on:

  "dependencies": {
    "@types/node": "^16.11.13",
    "typescript": "^4.5.4"
  }

Related:

You generally want an explicit interface when there are optional parameters

If any of the props is optional, is the case of the following example, and likely of most "options" params, then you likely want to explicitly define the type as mentioned at: https://stackoverflow.com/a/42108988/895245 because it is likely that the caller will need it sooner or later to build the options object step by step, related: How do I dynamically assign properties to an object in TypeScript?

E.g. consider the following working code:

const assert = require('assert')

interface myfuncOpts {
  myInt: number,
  myString?: string,
}

function myfunc({
  myInt,
  myString,
}: myfuncOpts) {
  return `${myInt} ${myString}`
}

const opts: myfuncOpts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

assert.strictEqual(
  myfunc(opts),
  '1 abc'
)

If we had defined myfuncOpts inline, then we wouldn't be able to do const opts: myfuncOpts, and then opts.myString = 'abc' would fail because in:

const opts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

the type of opts is deduced from the initialization, which does not contain myString.

I wish TypeScript would implement a system where you can say "use the type of such argument of such function". Then we could use inline argument type definitions, but still refer to the type somehow, it would be Nirvana.

Comments

2

There is no need for type nor interface. Just duck type it.

I just did

public getUserById(
    userId: string, 
    options: { onlyActive: boolean } 
           = { onlyActive: true }) : User

and call it by

const result = userService.getUserById(
    this.id, 
    {onlyActive:false});

or

const result = userService.getUserById(
    this.id);

As you can see I have an optional parameter with set default values; without any explicit declaration.

Comments

1

This is an improvement in readability over sending values, and it puts the values right there in the function call to be even more readable, just like good old named arguments. It doesn't suffer from any issues you wouldn't already suffer from if you were just sending unlabeled values, so it's a step up from that.

var includeIcons:boolean, includeChildren:boolean
await storageTank.save((includeIcons=false), (includeChildren=false))

Good for single developer projects, but not recommended for large projects where developers may not communicate with each other about changing function signatures. So it depends on the context.

Comments

1

I found there are 2 approaches:

sampleFunction1(params?: { param1: string, param2?: string }): void {
    console.info(`param1 is: ${params.param1}; param2 is: ${params.param2}`);
}
sampleFunction2(params: Partial<{ param1?: string, param2: string }> = { param1: 'default value' }): void {
    console.info(`param1 is: ${params.param1}; param2 is: ${params.param2}`);
}

this.sampleFunction1({param1: 'test1'});
this.sampleFunction2({param1: 'test1'});

I prefer the 2nd approach because I can set the default value.

Comments

-10

A simple solution would be to use meaningfully named local variables:

var mungo: boolean=true, mary: boolean=true, and: boolean=true, midge: boolean=true;

zapOn(mungo, mary, and, midge);

This is only a first step simple solution... following the principles of test-driven development: the test fails, get it working - test green (this recommendation), and refactor (if required, put the locals in an object and pass that to the function)

If you define the zapOn function in TypeScript so: zapOn({mungo, mary=false, midge , and}: TVInterface) {...} where TVInterface {mary?: boolean, mungo: boolean, and: boolean, midge: boolean} and pass the tv object = {mary: undefined, mungo: mungo, and: and, midge: midge} ie zapOn(tv) thanks to object destructuring at function parameters level the variables/parameters in the function will correctly line up... in the case of the „mary“ parameter we also set a default value of false.

2 Comments

It is not called call by the name parameter. What will happen if the function parameter order get change! Or users write in incorrect order, your answer is recommended for clean programming, but not calling the function by the name parameter.
This doesn't suffer from any problems you weren't already suffering from if you just used unlabeled values, and it's more readable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.