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
{{ message }}
Repository navigation
An initialized binding element should be optional even if its parent is initialized too #2469
The last line should not be an error because the y property has a default initializer. The error goes away if I remove the default initializer { x: 0, y: {}} on the parent.
I think the rule would be that even if we get the type of a parameter / variable / binding element from an initializer, we still have to traverse the binding pattern to determine whether binding properties are optional.
Note that you can observe this in another way too. If you remove the y from the parameter initializer, then you get an error on the binding element instead of the call site. But it's really the same issue:
functionfoo({ x, y ={}}={x: 0}){// Error that { x: number } doesn't have a y property}
Jason Freeman (@JsonFreeman) I don't think this is a bug. The order of precedence for determining the type of a variable or parameter is:
Type annotation
Initializer
Type implied by binding pattern (if applicable)
Consider this example:
interfaceFoo{x: number,y: string}functionf({ x, y ="hello"}: Foo){}
It would be odd indeed if the type of f's parameter isn't Foo as specified in the type annotation, but rather { x: number, y?: string } because y is magically made optional. I think the same logic applies to this example:
interfaceFoo{x: number,y: string}varfoo: Foo={x: 0,y: "test"};functionf({ x, y ="hello"}=foo){}
And, by extension, to this example:
functionf({ x, y ="hello"}={x: 0,y: "test"}){}
Furthermore, if a parameter has a type annotation or an initializer, it's type shouldn't be impacted by whether the parameter itself is specified as a simple identifier or a binding pattern. The two should be interchangeable with no effect on the type. (Indeed, refactorings and/or the declaration file generator might depend on this invariant.)
That said, we could potentially consider making properties optional in object literals if they are optional in the contextual type (which would apply here). I'm not really sure it's worth it, though.
It's funny. Reading your three examples, I actually think the first one should just be Foo, as you say. But in the second and third, y feels like it should be optional. The way I am thinking of it, a type annotation is a firm indication of the type. But an initializer, while it does specify the types of the binding elements, it doesn't tell you the whole story about optionality. I feel like it's not as strong an indicator of the type. And so when there is an intializer, but no type annotation, I feel like we do want to keep descending into the binding pattern to "optionalize" the elements that have their own initializers.
Furthermore, if a parameter has a type annotation or an initializer, it's type shouldn't be impacted by whether the parameter itself is specified as a simple identifier or a binding pattern.
Again, for a type annotation, I agree. For an initializer, I agree only if you converted the identifier to a binding pattern without adding initializers inside.
As a side note, converting an identifier to a binding pattern may indeed affect the type because of tuples:
(x=[0,1,2])=>{}// Array type([x,y,z]=[0,1,2])=>{}// Tuple type
The order of precedence for determining the type of a variable or parameter is:
Type annotation
Initializer
Type implied by binding pattern (if applicable)
Anders Hejlsberg (@ahejlsberg) Is that actually the case? In the following example, param is contextually typed as a string, and the local variable x has the type (s: string) => number.
var{ x =(str: string)=>+str}={x: param=>param.length};
But if we used the initializer first, wouldn't we acquire the type of x from the type of x from the RHS of the top-level initializer?
I feel like it's not as strong an indicator of the type.
Yes, I see what Jason Freeman (@JsonFreeman) is saying - it would be kind of ideal if the properties "zipped up" with respect to optionality when determining assignability. I can definitely get behind a very simple rule, but I get that there's also something to be said about flexibility here.
Daniel Rosenwasser (@DanielRosenwasser) the contextual typing you are talking about is that the RHS gets contextually typed by the type implied by the binding pattern. However, the final type of x is determined by the outer initializer (which got contextually typed using the inner initializer).
Anders Hejlsberg (@ahejlsberg) We could just do the contextual typing rule for optional properties like you mentioned. It would only work when the initializer is a literal though.
Activity
Correctly emit bidning pattern with initializer and rest
JsonFreeman commentedon Mar 23, 2015
Note that you can observe this in another way too. If you remove the y from the parameter initializer, then you get an error on the binding element instead of the call site. But it's really the same issue:
ahejlsberg commentedon Jul 23, 2015
Mohamed Hegazy (@mhegazy) Curious why you removed the bug label?
ahejlsberg commentedon Jul 23, 2015
Jason Freeman (@JsonFreeman) I don't think this is a bug. The order of precedence for determining the type of a variable or parameter is:
Consider this example:
It would be odd indeed if the type of f's parameter isn't Foo as specified in the type annotation, but rather
{ x: number, y?: string }because y is magically made optional. I think the same logic applies to this example:And, by extension, to this example:
Furthermore, if a parameter has a type annotation or an initializer, it's type shouldn't be impacted by whether the parameter itself is specified as a simple identifier or a binding pattern. The two should be interchangeable with no effect on the type. (Indeed, refactorings and/or the declaration file generator might depend on this invariant.)
That said, we could potentially consider making properties optional in object literals if they are optional in the contextual type (which would apply here). I'm not really sure it's worth it, though.
JsonFreeman commentedon Jul 24, 2015
It's funny. Reading your three examples, I actually think the first one should just be Foo, as you say. But in the second and third, y feels like it should be optional. The way I am thinking of it, a type annotation is a firm indication of the type. But an initializer, while it does specify the types of the binding elements, it doesn't tell you the whole story about optionality. I feel like it's not as strong an indicator of the type. And so when there is an intializer, but no type annotation, I feel like we do want to keep descending into the binding pattern to "optionalize" the elements that have their own initializers.
Again, for a type annotation, I agree. For an initializer, I agree only if you converted the identifier to a binding pattern without adding initializers inside.
As a side note, converting an identifier to a binding pattern may indeed affect the type because of tuples:
DanielRosenwasser commentedon Jul 24, 2015
Anders Hejlsberg (@ahejlsberg) Is that actually the case? In the following example,
paramis contextually typed as astring, and the local variablexhas the type(s: string) => number.But if we used the initializer first, wouldn't we acquire the type of
xfrom the type ofxfrom the RHS of the top-level initializer?Yes, I see what Jason Freeman (@JsonFreeman) is saying - it would be kind of ideal if the properties "zipped up" with respect to optionality when determining assignability. I can definitely get behind a very simple rule, but I get that there's also something to be said about flexibility here.
JsonFreeman commentedon Jul 24, 2015
Daniel Rosenwasser (@DanielRosenwasser) the contextual typing you are talking about is that the RHS gets contextually typed by the type implied by the binding pattern. However, the final type of x is determined by the outer initializer (which got contextually typed using the inner initializer).
Anders Hejlsberg (@ahejlsberg) We could just do the contextual typing rule for optional properties like you mentioned. It would only work when the initializer is a literal though.
ahejlsberg commentedon Sep 1, 2015
Jason Freeman (@JsonFreeman) Fix now available in #4598.