Description
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"}));