Skip to content

Include missing optional properties in contextually typed object literals #1774

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

Merged
merged 5 commits into from
Jan 22, 2015

Conversation

ahejlsberg
Copy link
Member

Fixes #1723.

In #919 we tightened the subtyping rules to require optional properties to be present in subtypes. That's still the right thing to do, but it can have unintended consequences for overload resolution (which relies on subtyping) in combination with object literals.

With this PR, when an object literal is contextually typed by a type T, any optional properties in T that are omitted in the object literal are included in the resulting type, thus ensuring the resulting type is a subtype of T when all that was omitted was optional properties (whew!). For example

interface Item {
    name: string;
    description?: string;
}

declare function foo(item: Item): string;
declare function foo(item: any): number;

var x = foo({ name: "Sprocket" });  // x of type string
var y = foo({ name: "Sprocket", description: "Bumpy wheel" });  // y of type string
var z = foo({ a: 10 });  // z of type number

In the first call to foo above, contextual typing of the object literal adds the missing optional property. Thus, the argument expression becomes a subtype of Item, and the first overload is chosen (as one would expect).

This change does at times affect error reporting. For example

var f: Item = { naem: "Hello" };

Here we'll now report

t.ts(11,5): error TS2322: Type '{ naem: string; description?: string; }' is not assignable to type 'Item'.
  Property 'name' is missing in type '{ naem: string; description?: string; }'.

Depending on your point of view it may be surprising that the reported type includes an optional property that you didn't actually write (but which is there because of your context).

@JsonFreeman
Copy link
Contributor

This breaks the following:

declare function foo<T>(param: { [s: string]: T; x?: T }): T;
var f = foo({ y: "" }); // returns type string | T
var g = foo({ }); // returns type T

By copying the type of x from the parameter type to the argument, it gets to participate in the union type for the indexer. So the type parameter leaks.

@JsonFreeman
Copy link
Contributor

Simpler example, with no indexer:

declare function foo<T>(param: { x?: T }): T;
var f = foo({ }); returns T

@ahejlsberg
Copy link
Member Author

@JsonFreeman Good catch on the leaky type parameter. Fixed now.

@ahejlsberg
Copy link
Member Author

@JsonFreeman That should do it.

@JsonFreeman
Copy link
Contributor

👍

ahejlsberg added a commit that referenced this pull request Jan 22, 2015
Include missing optional properties in contextually typed object literals
@ahejlsberg ahejlsberg merged commit 67476f1 into master Jan 22, 2015
@ahejlsberg ahejlsberg deleted the objectLiteralSubtyping branch January 22, 2015 23:36
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Overload on constants requires cast for resolution
3 participants