Skip to content

Check argument names in function calls #46455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 tasks done
Finesse opened this issue Oct 21, 2021 · 4 comments
Closed
5 tasks done

Check argument names in function calls #46455

Finesse opened this issue Oct 21, 2021 · 4 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@Finesse
Copy link

Finesse commented Oct 21, 2021

Suggestion

πŸ” Search Terms

  • named arguments
  • name argument function call
  • hide property name
  • hide attribute name
  • remove property name

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

This suggestion is very similar to #467, but it has another purpose and motivation.

I'd like to have an ability to specify argument names in function calls. TypeScript will emit an error if an argument name in a function call expression doesn't match the argument name in the function declaration.

function foo(name: string, surname: string) {}

foo(surname: 'Doe', name: 'John'); // error, both the argument names don't match
foo(foo: 'John', bar: 'Doe'); // error
foo(name: 'John', surname: 'Doe'); // ok
foo('John', 'Doe'); // ok, argument names are optional
foo(name: 'John', 'Doe'); // ok
foo('Doe', name: 'John'); // error, the second argument name doesn't match

TypeScript compiler will just remove the argument names from the function invocation expressions. The argument names in call expressions don't affect the output code at all.

This idea can be spread further on arrays with named elements, but this is off-topic here.

πŸ“ƒ Motivating Example

The motivation is a need to hide my source code. Let's say I use TypeScript with Terser, I need to make a function with many arguments and hide the argument names from the output code.

The following source code will give the result I want:

function secretFunctionName(
  secretName: string,
  anotherSecretName: string,
  yetAnotherSecretName: string,
  theMostSecretName: string
) {
    return [secretName, anotherSecretName, yetAnotherSecretName, theMostSecretName].join();
}

console.log(secretFunctionName('foo', 'bar', 'baz', 'baq'));
console.log(secretFunctionName('1', '2', '3', '4'));

// TypeScript + Terser output:
function o(o,n,l,a){return[o,n,l,a].join()}
console.log(o("foo","bar","baz","baq"));
console.log(o("1","2","3","4"));

But it's prone to human mistake because all the arguments have the same type so there will be no TypeScript error if a pair of arguments in a function call is swapped.

A common solution for such mistakes is passing arguments in an object:

interface Arguments {
  secretName: string,
  anotherSecretName: string,
  yetAnotherSecretName: string,
  theMostSecretName: string
}

function secretFunctionName({
  secretName,
  anotherSecretName,
  yetAnotherSecretName,
  theMostSecretName
}: Arguments) {
    return [secretName, anotherSecretName, yetAnotherSecretName, theMostSecretName].join();
}

console.log(secretFunctionName('foo', 'bar', 'baz', 'baq'));
console.log(secretFunctionName('1', '2', '3', '4'));

// TypeScript + Terser output:
function e({secretName:e,anotherSecretName:t,yetAnotherSecretName:r,theMostSecretName:m}){return[e,t,r,m].join()}
console.log(e({secretName:"foo",anotherSecretName:"bar",yetAnotherSecretName:"baz",theMostSecretName:"baq"}));
console.log(e({secretName:"1",anotherSecretName:"2",yetAnotherSecretName:"3",theMostSecretName:"4"}));

That it, the secret argument names have got to the output code. Also, the output code is much bigger.

The syntax I suggest will solve both problems (will conceal the argument names and help preventing human mistakes):

function secretFunctionName(
  secretName: string,
  anotherSecretName: string,
  yetAnotherSecretName: string,
  theMostSecretName: string
) {
    return [secretName, anotherSecretName, yetAnotherSecretName, theMostSecretName].join();
}

console.log(secretFunctionName(
  secretName: 'foo',
  anotherSecretName: 'bar',
  yetAnotherSecretName: 'baz',
  theMostSecretName: 'baq'
));
console.log(secretFunctionName(
  secretName: '1',
  anotherSecretName: '2',
  yetAnotherSecretName: '3',
  theMostSecretName: '4'
));

// TypeScript + Terser output:
function o(o,n,l,a){return[o,n,l,a].join()}
console.log(o("foo","bar","baz","baq"));
console.log(o("1","2","3","4"));

πŸ’» Use Cases

Besides concealing the source code, the new syntax will help producing smaller JS scripts. Developers will achieve it by replacing argument objects with regular arguments and function call checks.

At the moment I can solve my issue by taking a risk of mistakes by using regular arguments as shown in the first code snippet in the "Motivating Example" section.

Another solution is using an argument object with a const enum which is bulky:

const enum ArgumentName {
  Secret,
  AnotherSecret,
  YetAnotherSecret,
  TheMostSecret
}

interface Arguments {
  [ArgumentName.Secret]: string,
  [ArgumentName.AnotherSecret]: string,
  [ArgumentName.YetAnotherSecret]: string,
  [ArgumentName.TheMostSecret]: string
}

function secretFunctionName(args: Arguments) {
    return [
      args[ArgumentName.Secret],
      args[ArgumentName.AnotherSecret],
      args[ArgumentName.YetAnotherSecret],
      args[ArgumentName.TheMostSecret]
    ].join();
}

console.log(secretFunctionName({
  [ArgumentName.Secret]: 'foo',
  [ArgumentName.AnotherSecret]: 'bar',
  [ArgumentName.YetAnotherSecret]: 'baz',
  [ArgumentName.TheMostSecret]: 'baq'
}));
console.log(secretFunctionName({
  [ArgumentName.Secret]: '1',
  [ArgumentName.AnotherSecret]: '2',
  [ArgumentName.YetAnotherSecret]: '3',
  [ArgumentName.TheMostSecret]: '4'
}));

// TypeScript + Terser output:
function o(o){return[o[0],o[1],o[2],o[3]].join()}
console.log(o({0:"foo",1:"bar",2:"baz",3:"baq"}));
console.log(o({0:"1",1:"2",2:"3",3:"4"}));
@MartinJohns
Copy link
Contributor

I really don't see how this is any different than #467.

@Finesse
Copy link
Author

Finesse commented Oct 21, 2021

@MartinJohns The difference are:

  1. The argument order is always the same, therefore the types don't affect the output code.
  2. The goal is reducing the output size and hiding the source code (instead of having an ability to skip arguments). In Named parameters/arguments in function callΒ #467 the conversation leads to a thought that argument object solves the issue (it's not a solution in my case).

@DanielRosenwasser
Copy link
Member

This really just isn't within our scope given that it can conflict with future JavaScript syntax. In a sense, you could consider this "syntax sugar for JS" but perhaps this goal should be more explicitly mentoned in the issue template.

@DanielRosenwasser DanielRosenwasser added Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript labels Nov 4, 2021
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Out of Scope" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants