-
Notifications
You must be signed in to change notification settings - Fork 12.8k
An initialized binding element should be optional even if its parent is initialized too #2469
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
Comments
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: function foo({ x, y = {}} = { x: 0 }) { // Error that { x: number } doesn't have a y property
} |
@mhegazy Curious why you removed the bug label? |
@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: interface Foo { x: number, y: string }
function f({ 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 interface Foo { x: number, y: string }
var foo: Foo = { x: 0, y: "test" };
function f({ x, y = "hello" } = foo) {
} And, by extension, to this example: function f({ 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.
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 |
@ahejlsberg Is that actually the case? In the following example, var { x = (str: string) => +str } = { x: param => param.length }; But if we used the initializer first, wouldn't we acquire the type of
Yes, I see what @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. |
@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). @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. |
@JsonFreeman Fix now available in #4598. |
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.
The text was updated successfully, but these errors were encountered: