54

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user7425470
  • 735
  • 1
  • 6
  • 13
  • 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. – deceze Feb 08 '17 at 09:10
  • @deceze http://stackoverflow.com/questions/11796093/named-parameters-in-javascript the post by Ray Perea (actually an JS-Object) – user7425470 Feb 08 '17 at 09:26
  • And how does that *not* work for you…? – deceze Feb 08 '17 at 09:27
  • Stepping into typescript the object will look strange, take a look http://pasteboard.co/vNZ5OEg16.png (similar example) – user7425470 Feb 08 '17 at 09: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. – Emilio Platzer Mar 06 '18 at 14:15

5 Answers5

67

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
}
Paleo
  • 21,831
  • 4
  • 65
  • 76
17

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
    });
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
6

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0

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.

toddmo
  • 20,682
  • 14
  • 97
  • 107
-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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kieran Ryan
  • 593
  • 2
  • 8
  • 18
  • 3
    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. – Mohammad Nikravan Dec 04 '18 at 23:41
  • This doesn't suffer from any problems you weren't already suffering from if you just used unlabeled values, and it's more readable. – toddmo Dec 08 '22 at 14:58