You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Would it make sense to introduce the following syntax to reduce duplication of code?
functionfoo({paramA as number, paramB as string,paramCas boolean,paramD? as Object}){}functionbar([paramAasnumber,paramBasstring,paramCasboolean,paramDasObject]){}
Originally I wanted to suggest to use the colon ":" instead of the "as", but since the colon is used for renaming destructured parametes, the "as" is the only option I guess.
With this syntax change the following would be possible too I guess:
let{paramA as number, paramB as string,paramCas boolean, paramD asObject}=anyObject;let{paramA: aasnumber,paramB: basstring,paramC: casboolean,paramD: dasObject}=anyObject;let[paramAasnumber,paramBasstring,paramCasboolean,paramDasObject]=anyArray;
We have talked about this as a possibility during the design of the destructuring support. a few thoughts,
as should have been using for the rename operator instead of :, that maps well to the import case where as is used to rename an import or export name binding, e.g. import {a as b} from "m" or import * as ns from "m".
there is value in consistency that types always come after a :. adding another operator would increase complexity.
there is a possibility of the committee (TC39) using as in the future, again to align with imports as an alternative for renaming. at this point we would be in a bind. we have the : domain almost officially reserved for types.
ultimately, every new syntax added is added complexity, for the user to learn and recognize, and for the compiler code base to maintain, so usually we try to limit complexity unless the value warrants it. and for destructuring, the syntax is already complex to start with, and we did not want to add yet another layer on top.
I think we're going to need to decline this one. Adding any more syntax here would put us in a tough spot, and on the whole, I think this would confuse users quite a bit.
I have to second this request. Without some solution to this issue, I'm strongly inclined to avoid parameter destructuring in TypeScript even if it means going back to the days of myMethod(param1, null, null, 2, null, null, true). It's just too much overhead to maintain two separate lists of parameters, one for types and another for default values.
Note that this issue combined with #5326 make destructuring extremely verbose for class constructors, negating a ton of improvements I've made to my ES6 code that I'd lose moving to TypeScript.
Clayton Watts (@cletusw)--noImplicitAny is fully optional. You can use parameter destructuring and all of the properties will be of type any, just like they would had your written your code for Babel. So you would lose absolutely nothing.
MichaelBuen I fully agree. The rename syntax is not only awkward and incompatible it is also inconsistent with imports which obviously are a natural analogy even though they have different semantics.
It is also not that the committee didn't envision TypeScript or Flow, as typed ECMAScript dialects existed before the standard was finalized.
As best I can tell, the reason for using the : comes from a desire for symmetry between composing and decomposing object literals.
.. which entails going to each variable and surrounding each of them with parenthesis, it's tiresome and makes the code looks so busy. It would be easier to just remove the colon:
functionfoo({surname string, birthDate Date})
And when it's need to be renamed, just use the already established syntax for renaming:
Maybe ES6 committee decided that renaming destructured object would be the norm, so they choose a terse syntax to rename something. But why ES6 didn't dream of JavaScript of having types someday, or having TypeScript being part of the browser? Had they thought of it, they would not choose colon for renaming variable, they would use as for renaming, which is symmetrical with import statement. Another alternative would be is not to use an operator at all, same with SQL:
Will just wait for WebAssembly to take off, and wait for a language that has a nicer syntax :) It might also be our chance for a language to have a decimal type.
I highly suggest this syntax..
functionfoo({surname string, birthDate Date})
..it's not really weird to remove an operator for declaring types, e.g.
Whereas this one would cause breaking change when as would be introduced for renaming destructured object. Both surname and birthDate would have any type when as would be used for renaming in the future:
functionfoo({surname as string, birthDate asDate})const{surname as string, birthDate asDate}=person;
I was expecting the new name would be on left, and is symmetrical with how the object is composed:
const{apellido: lastname, birthDate}=person;
Perhaps they decided that it would be easier for users to scan/read all the object's original property names when they are on left. And they would like the users to mentally read the colon as as.
Personally I also think as would have been better. I had the same intuition, thinking the names were backward. Then again maybe it's because I was used to looking at types in that position. Regardless, I see no reason why the syntax is different than that used for aliasing imports...
So much pain right now to use destructuring with --noImplicitAny or --strict.
Plain space separation could be more readable if type would prepend name, like in C or Dart.
Added <> parentheses, would add readability too. It's better localized than (name:type):label and already used in type casting.
Plain space separation could be more readable if type would prepend name, like in C or Dart.
Obviously this is subjective, but name: type has many advantages over type name from many different angles. There are many reasons for this, but Eric Lippert makes a good case for it so check out item 5 on this list
Regardless, while I definitely meant what I said about the inline rename syntax being confusing and unfortunate, the ship has long since sailed and so this is not going to change.
Reacted by Gregory, Tamás Pap, ugeng, Szymon Stasik and nexus
React components are a perfect example for destructuring, since props interfaces are never reused and keeping them separate smears the description and pollutes namespace. Latter could be fixed by wrapping them in namespace ComponentName {}, but it still adds more code to read.
I find I avoid destructuring ad hoc parameters in TypeScript because of this. Maybe good to define common types, but I find instead that I use indexed parameters more than I should instead. The decision to leave this out harms code quality, in my view.
Reacted by Patrick Oswald, Clayton Watts, Eryk Napierała, Shaun Cutts, Andrey Syschikov, Aliaksei Kislou, Mariusz Pawelski, Axel Rauschmayer, Antoine Latournerie, Tom Hughes and 2 more
I was exploring options with React and wanted to add what I had found in my experiments:
// Destructuring as an arg// Pros:// - Type Inference!// - Same as ES6!// - IDEAL!exportconstC2=({ def ='DEFAULT'})=>{// const opt: string (optional? lost: see NOTE above)return(<Text>{def}</Text>);};exportconstC2_Usage=()=>{return(<C2></C2>);};// Unused Default Object (HACKY BUT GOOD) - WINNER FOR REACT!!!// Pros:// - Forces default value for all optional arguments// - ES6 Compatible// - Closest to IDEAL solution when all arguments are optional// Cons:// - Pseudo-Required Arguments (Works fine for React Components)// - Requires Duplication of required variable namesexportconstD3=({ req, opt ='', def ='DEFAULT'}={req: ''})=>{// const opt: string (optional? lost: see NOTE above)return(<Text>{req +opt+def}</Text>);};exportconstD3_Usage=()=>{return(<D3req='REQUIRED_TRUE'></D3>);};exportconstD3_Call=()=>{// CON: This is PossibleD3();// But if any object is given, it works rightD3({req: ''});};
This pattern will work well for React components, but it doesn't work great for normal functions because it's possible to call with no argument object.
Naming parameters via object literals is an increasingly popular pattern in JavaScript. TC39 even abandoned real named parameters in favor of this pattern.
Therefore, TypeScript should really support it as well as it can. This is how it could be done:
// No type annotation{x}{x=123}{xin: xout}{xin: xout=123}// Type annotation via parentheses{(x: number)}{(x: number)=123}{xin: (xout: number)}{xin: (xout: number)=123}// Type annotation via `as` operator{xasnumber}{xasnumber=123}{xin: xoutasnumber}{ xin: xoutasnumber=123}
Compare:
// Current TypeScript:functionfunc({firstProp, secondProp, thirdProp}:
{firstProp: number,secondProp: number,thirdProp: number}={}){/* ··· */}// Better support for named parameters:functionfunc({firstProp as number, secondProp as number, thirdProp as number}={}){/* ··· */}
The latter notation is much closer to the “simulated named parameters” idea.
Real-world example:
Current TypeScript (“Showoff” is the name of my slide framework):
Once again: less redundancy and the parameters are directly connected with their types.
Similar issue with multiple return values:
Example in my code:
parseIndexFromDir() returns multiple values, but we are only interested in pageNode here.
I wanted to add a type annotation, to make things explicit.
// Current TypeScript:const{pageNode}: {pageNode: PageNode}=parseIndexFromDir(···);// With better syntax:const{pageNode asPageNode}=parseIndexFromDir(···);// Alternatively:const{(pageNode: PageNode)}=parseIndexFromDir(···);
Reacted by Tom, Joshua Marantz, Christoph Zwerschke, Christoforos Varakliotis, Kees Kluskens, Mario Lamontagne, Ihor Yemets, Lazar Ljubenović, t7yang, Simon Cave and 62 more
W.r.t. “the colon should really be as”: I don’t think of object destructuring as renaming, more like a transparent sheet that you put on top of data. That is:
My interpretation:
{x: y} extracts property x and assigns it to variable y
{x} is syntactic sugar for {x: x}
Versus:
{x: y} renames x to y
This interpretation makes even more sense if you nest destructurings:
let {b: {b, c}} = obj;
b is not renamed, but further destructured.
Reacted by Qwerty (Vítězslav Ackermann Ferko), Jon Rimmer, cesare soldini, Kumar Harsh, Jan, Ilpo Toni and Charlike Mike Reagent
function func({{ // use double curly brace
a: number,
b: string = 'x',
c as d: boolean,
e as {x1, y1}: {x1: number, y1: number},
f as {{x2: number, y2: number}},
}}) { ... }
function func({{ // use double curly brace
a: number,
b: string = 'x',
c as d: boolean,
e as {x1, y1}: {x1: number, y1: number},
f as {{x2: number, y2: number}},
}}) { ... }
However, I don't understand the purpose of writing c, e, and f that are immediately destructured. And I certainly don't like using as for anything but type casting.
Maybe something like:
function func({{
// Type Only (Required Parameter)
a: number,
// Destructuring with no defaults (Required Parameter)
{{b1:number, b2:number}},
// Default Value (Optional Parameter)
c = 'x',
// Destructuring with default or optional values (Optional Parameter)
{{d1 = 0, d2 = 0, d3 : number?}},
}}) { ... }
I don't understand the purpose of writing c, e, and f that are immediately destructured.
Because it's object destructuring, you need to specify name of parameters.
So 2nd and 4th argument on your example has no name. Maybe it cannot be destructured.
Activity
mhegazy commentedon Mar 18, 2016
We have talked about this as a possibility during the design of the destructuring support. a few thoughts,
asshould have been using for the rename operator instead of:, that maps well to the import case whereasis used to rename an import or export name binding, e.g.import {a as b} from "m"orimport * as ns from "m".:. adding another operator would increase complexity.asin the future, again to align with imports as an alternative for renaming. at this point we would be in a bind. we have the:domain almost officially reserved for types.DanielRosenwasser commentedon Mar 20, 2016
I think we're going to need to decline this one. Adding any more syntax here would put us in a tough spot, and on the whole, I think this would confuse users quite a bit.
cletusw commentedon Mar 9, 2017
I have to second this request. Without some solution to this issue, I'm strongly inclined to avoid parameter destructuring in TypeScript even if it means going back to the days of
myMethod(param1, null, null, 2, null, null, true). It's just too much overhead to maintain two separate lists of parameters, one for types and another for default values.cletusw commentedon Apr 11, 2017
Note that this issue combined with #5326 make destructuring extremely verbose for class constructors, negating a ton of improvements I've made to my ES6 code that I'd lose moving to TypeScript.
aluanhaddad commentedon Apr 12, 2017
Clayton Watts (@cletusw)
--noImplicitAnyis fully optional. You can use parameter destructuring and all of the properties will be of typeany, just like they would had your written your code for Babel. So you would lose absolutely nothing.cletusw commentedon Apr 12, 2017
This is true, although I'm relying heavily on
noImplicitAnyto help me find all the places that need types, so I would be loathe to turn that off.aluanhaddad commentedon Jul 23, 2017
MichaelBuen I fully agree. The rename syntax is not only awkward and incompatible it is also inconsistent with imports which obviously are a natural analogy even though they have different semantics.
It is also not that the committee didn't envision TypeScript or Flow, as typed ECMAScript dialects existed before the standard was finalized.
As best I can tell, the reason for using the
:comes from a desire for symmetry between composing and decomposing object literals.MichaelBuen commentedon Jul 23, 2017
How about this?
If a code..
.. need to be improved to use destructured object, instead of this proposed syntax..
.. which entails going to each variable and surrounding each of them with parenthesis, it's tiresome and makes the code looks so busy. It would be easier to just remove the colon:
And when it's need to be renamed, just use the already established syntax for renaming:
Maybe ES6 committee decided that renaming destructured object would be the norm, so they choose a terse syntax to rename something. But why ES6 didn't dream of JavaScript of having types someday, or having TypeScript being part of the browser? Had they thought of it, they would not choose colon for renaming variable, they would use
asfor renaming, which is symmetrical with import statement. Another alternative would be is not to use an operator at all, same with SQL:Not using
aswould be just as fine:So renaming surname to apellido would be:
And then when in the not-so-distant future that JavaScript would have types. Boom! they could just use the established syntax for declaring types:
Too late.
Will just wait for WebAssembly to take off, and wait for a language that has a nicer syntax :) It might also be our chance for a language to have a decimal type.
I highly suggest this syntax..
..it's not really weird to remove an operator for declaring types, e.g.
Another advantage of the above over this one..
..is if ECMAScript committee decided to make the renaming use the
askeyword so there's symmetry with import'sas, this won't cause breaking change:Whereas this one would cause breaking change when
aswould be introduced for renaming destructured object. Both surname and birthDate would haveanytype whenaswould be used for renaming in the future:MichaelBuen commentedon Jul 23, 2017
Indeed, destructuring using colon operator gives symmetry for composing and decomposing between object literals.
However, when I first use destructuring it felt reversed:
I was expecting the new name would be on left, and is symmetrical with how the object is composed:
Perhaps they decided that it would be easier for users to scan/read all the object's original property names when they are on left. And they would like the users to mentally read the colon as
as.aluanhaddad commentedon Jul 23, 2017
Personally I also think
aswould have been better. I had the same intuition, thinking the names were backward. Then again maybe it's because I was used to looking at types in that position. Regardless, I see no reason why the syntax is different than that used for aliasing imports...garkin commentedon Jul 28, 2017
Related Flow issue facebook/flow/issues/235
So much pain right now to use destructuring with
--noImplicitAnyor--strict.Plain space separation could be more readable if type would prepend name, like in C or Dart.
Added
<>parentheses, would add readability too. It's better localized than(name:type):labeland already used in type casting.Or just use a second colon
name[:[label][:type]]as some functional languages do:aluanhaddad commentedon Jul 28, 2017
garkin
Ultimately, I think it is best to write
from a maintainability perspective.
Obviously this is subjective, but
name: typehas many advantages overtype namefrom many different angles. There are many reasons for this, but Eric Lippert makes a good case for it so check out item 5 on this listRegardless, while I definitely meant what I said about the inline rename syntax being confusing and unfortunate, the ship has long since sailed and so this is not going to change.
garkin commentedon Jul 29, 2017
React components are a perfect example for destructuring, since
propsinterfaces are never reused and keeping them separate smears the description and pollutes namespace. Latter could be fixed by wrapping them innamespace ComponentName {}, but it still adds more code to read.tjpalmer commentedon Oct 10, 2017
I find I avoid destructuring ad hoc parameters in TypeScript because of this. Maybe good to define common types, but I find instead that I use indexed parameters more than I should instead. The decision to leave this out harms code quality, in my view.
ricklove commentedon Oct 28, 2017
I was exploring options with React and wanted to add what I had found in my experiments:
This pattern will work well for React components, but it doesn't work great for normal functions because it's possible to call with no argument object.
rauschma commentedon Mar 4, 2018
Naming parameters via object literals is an increasingly popular pattern in JavaScript. TC39 even abandoned real named parameters in favor of this pattern.
Therefore, TypeScript should really support it as well as it can. This is how it could be done:
Compare:
The latter notation is much closer to the “simulated named parameters” idea.
Real-world example:
Current TypeScript (“Showoff” is the name of my slide framework):
With a better notation:
Alternatively:
Once again: less redundancy and the parameters are directly connected with their types.
Similar issue with multiple return values:
Example in my code:
parseIndexFromDir()returns multiple values, but we are only interested inpageNodehere.rauschma commentedon Mar 4, 2018
W.r.t. “the colon should really be
as”: I don’t think of object destructuring as renaming, more like a transparent sheet that you put on top of data. That is:{x: y}extracts propertyxand assigns it to variabley{x}is syntactic sugar for{x: x}{x: y}renamesxtoyThis interpretation makes even more sense if you nest destructurings:
bis not renamed, but further destructured.scf4 commentedon Apr 7, 2018
Axel Rauschmayer (@rauschma) I hope we can see this soon!
odiak commentedon Apr 17, 2018
How about syntax like this?
In current TypeScript, it means:
I think it don't break existing syntax and it's not so strange syntax.
rauschma commentedon Apr 20, 2018
Last update on this topic:
ricklove commentedon Apr 20, 2018
Kaido Iwamoto (@odiak) Interesting Idea
However, I don't understand the purpose of writing c, e, and f that are immediately destructured. And I certainly don't like using
asfor anything but type casting.Maybe something like:
odiak commentedon Apr 21, 2018
Rick Love (@ricklove)
Because it's object destructuring, you need to specify name of parameters.
So 2nd and 4th argument on your example has no name. Maybe it cannot be destructured.