-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Destructuring with type annotations #7576
Comments
We have talked about this as a possibility during the design of the destructuring support. a few thoughts,
|
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 |
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. |
@cletusw |
This is true, although I'm relying heavily on |
@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 |
How about this? function foo({surname string, birthDate Date}) If a code.. function foo(dto: {surname: string, birthDate: Date}) .. need to be improved to use destructured object, instead of this proposed syntax.. function foo({(surname: string), (birthDate: Date)}) .. 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: function foo({surname string, birthDate Date}) And when it's need to be renamed, just use the already established syntax for renaming: function foo({surname string: apellido, birthDate Date: fecha}) 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 SELECT * FROM Person as p Not using SELECT * FROM Person p So renaming surname to apellido would be: function foo({surname apellido, birthDate fecha})
const {surname apellido, birthDate fecha} = person; 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: function foo({surname apellido: string, birthDate fecha: Date})
const {surname apellido: string, birthDate fecha: Date} = person;
function foo({surname: string, birthDate: Date})
const {surname: string, birthDate: Date} = person; 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.. function foo({surname string, birthDate Date}) ..it's not really weird to remove an operator for declaring types, e.g. CREATE TABLE Person (
PersonID int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
); Another advantage of the above over this one.. function foo({surname as string, birthDate as Date}) ..is if ECMAScript committee decided to make the renaming use the function foo({surname string as apellido, birthDate Date as fecha})
function foo({surname as apellido, birthDate as fecha})
const {surname string as apellido, birthDate Date as fecha} = person;
const {surname as apellido, birthDate as fecha} = person; Whereas this one would cause breaking change when function foo({surname as string, birthDate as Date})
const {surname as string, birthDate as Date} = person; |
Indeed, destructuring using colon operator gives symmetry for composing and decomposing between object literals. However, when I first use destructuring it felt reversed: const person = {lastname: 'Eich', birthDate: Date()};
const {lastname: apellido, birthDate} = person;
console.log(apellido); 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 |
Personally I also think |
Related Flow issue facebook/flow/issues/235 So much pain right now to use destructuring with Plain space separation could be more readable if type would prepend name, like in C or Dart. const Component = ({ count:c, max:m }: { count:number, max:number }) => <div>{c}/{m}</div>;
const Component = ({ count number :c, max number :m }) => <div>{c}/{m}</div>; const Component = ({ number count:c, number max:m }) => <div>{c}/{m}</div>;
const Component = ({ <number> count:c, <number> max:m }) => <div>{c}/{m}</div>; Or just use a second colon const Component = ({ count:c:number, max:m:number }) => <div>{c}/{m}</div>;
const Component = ({ count::number, max::number }) => <div>{count}/{max}</div>; |
Ultimately, I think it is best to write const Component = ({count, max}: Props) => <div>{count}/{max}</div>;
type Props = {count: number, max: number}; from a maintainability perspective.
Obviously this is subjective, but 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. |
React components are a perfect example for destructuring, since |
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. |
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!
export const C2 = ({ def = 'DEFAULT' }) => {
// const opt: string (optional? lost: see NOTE above)
return (<Text>{def}</Text>);
};
export const C2_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 names
export const D3 = ({ req, opt = '', def = 'DEFAULT' } = { req: '' }) => {
// const opt: string (optional? lost: see NOTE above)
return (<Text>{req + opt + def}</Text>);
};
export const D3_Usage = () => {
return (<D3 req='REQUIRED_TRUE'></D3>);
};
export const D3_Call = () => {
// CON: This is Possible
D3();
// But if any object is given, it works right
D3({ 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
{ x as number }
{ x as number = 123 }
{ xin: xout as number }
{ xin: xout as number = 123 } Compare: // Current TypeScript:
function func({firstProp, secondProp, thirdProp}:
{firstProp: number, secondProp: number, thirdProp: number} = {}) {
/* ··· */
}
// Better support for named parameters:
function func({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): function slidesToNodeTree(
{conf, configShowoff, inputDir, slideDeckDir, slideFileName, parentPartNode, visitedSlideFiles}
: {conf: ConfigSlideLink, configShowoff: ConfigShowoff, inputDir: string,
slideDeckDir: ServePath, slideFileName: string, parentPartNode: PartNode,
visitedSlideFiles: SlideFileDesc[]}) {
···
} With a better notation: function slidesToNodeTree(
{ conf as ConfigSlideLink, configShowoff as ConfigShowoff, inputDir as string,
slideDeckDir as ServePath, slideFileName as string, parentPartNode as PartNode,
visitedSlideFiles as SlideFileDesc[]}) {
···
} Alternatively: function slidesToNodeTree(
{ (conf: ConfigSlideLink), (configShowoff: ConfigShowoff), (inputDir: string),
(slideDeckDir: ServePath), (slideFileName: string), (parentPartNode: PartNode),
(visitedSlideFiles: SlideFileDesc[])}) {
···
} Once again: less redundancy and the parameters are directly connected with their types. Similar issue with multiple return values: Example in my code:
// Current TypeScript:
const {pageNode}: {pageNode: PageNode} = parseIndexFromDir(···);
// With better syntax:
const {pageNode as PageNode} = parseIndexFromDir(···);
// Alternatively:
const { (pageNode: PageNode) } = parseIndexFromDir(···); |
W.r.t. “the colon should really be
This interpretation makes even more sense if you nest destructurings:
|
@rauschma I hope we can see this soon! |
How about syntax like this?
In current TypeScript, it means: function func({
a,
b = 'x',
c: d,
e: {x1, y1},
f: {x2, y2},
}: {
a: number,
b?: string,
c: boolean,
e: {x1: number, y1: number},
f: {x2: number, y2: number},
}) { ... } I think it don't break existing syntax and it's not so strange syntax. |
Last update on this topic:
|
@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 Maybe something like:
|
Because it's object destructuring, you need to specify name of parameters. |
Using destructuring in functions signatures is a relief, but when combining it with type declarations there is a lot of duplication necessary:
Would it make sense to introduce the following syntax to reduce duplication of code?
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:
The text was updated successfully, but these errors were encountered: