Skip to content

Commit 8a72229

Browse files
committed
Merge pull request microsoft#7298 from Microsoft/contextually-type-binding-initializers
Contextually type binding initializers
2 parents 98a2458 + 4c4bc61 commit 8a72229

8 files changed

+454
-6
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,7 +2604,7 @@ namespace ts {
26042604
return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((<ComputedPropertyName>name).expression.kind);
26052605
}
26062606

2607-
// Return the inferred type for a binding element
2607+
/** Return the inferred type for a binding element */
26082608
function getTypeForBindingElement(declaration: BindingElement): Type {
26092609
const pattern = <BindingPattern>declaration.parent;
26102610
const parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent);
@@ -2630,6 +2630,9 @@ namespace ts {
26302630
// computed properties with non-literal names are treated as 'any'
26312631
return anyType;
26322632
}
2633+
if (declaration.initializer) {
2634+
getContextualType(declaration.initializer);
2635+
}
26332636

26342637
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
26352638
// or otherwise the type of the string index signature.
@@ -7825,11 +7828,14 @@ namespace ts {
78257828
return undefined;
78267829
}
78277830

7828-
// In a variable, parameter or property declaration with a type annotation, the contextual type of an initializer
7829-
// expression is the type of the variable, parameter or property. Otherwise, in a parameter declaration of a
7830-
// contextually typed function expression, the contextual type of an initializer expression is the contextual type
7831-
// of the parameter. Otherwise, in a variable or parameter declaration with a binding pattern name, the contextual
7832-
// type of an initializer expression is the type implied by the binding pattern.
7831+
// In a variable, parameter or property declaration with a type annotation,
7832+
// the contextual type of an initializer expression is the type of the variable, parameter or property.
7833+
// Otherwise, in a parameter declaration of a contextually typed function expression,
7834+
// the contextual type of an initializer expression is the contextual type of the parameter.
7835+
// Otherwise, in a variable or parameter declaration with a binding pattern name,
7836+
// the contextual type of an initializer expression is the type implied by the binding pattern.
7837+
// Otherwise, in a binding pattern inside a variable or parameter declaration,
7838+
// the contextual type of an initializer expression is the type annotation of the containing declaration, if present.
78337839
function getContextualTypeForInitializerExpression(node: Expression): Type {
78347840
const declaration = <VariableLikeDeclaration>node.parent;
78357841
if (node === declaration.initializer) {
@@ -7845,6 +7851,18 @@ namespace ts {
78457851
if (isBindingPattern(declaration.name)) {
78467852
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ true);
78477853
}
7854+
if (isBindingPattern(declaration.parent)) {
7855+
const parentDeclaration = declaration.parent.parent;
7856+
const name = declaration.propertyName || declaration.name;
7857+
if (isVariableLike(parentDeclaration) &&
7858+
parentDeclaration.type &&
7859+
!isBindingPattern(name)) {
7860+
const text = getTextOfPropertyName(name);
7861+
if (text) {
7862+
return getTypeOfPropertyOfType(getTypeFromTypeNode(parentDeclaration.type), text);
7863+
}
7864+
}
7865+
}
78487866
}
78497867
return undefined;
78507868
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//// [contextuallyTypedBindingInitializer.ts]
2+
interface Show {
3+
show: (x: number) => string;
4+
}
5+
function f({ show = v => v.toString() }: Show) {}
6+
function f2({ "show": showRename = v => v.toString() }: Show) {}
7+
function f3({ ["show"]: showRename = v => v.toString() }: Show) {}
8+
9+
interface Nested {
10+
nested: Show
11+
}
12+
function ff({ nested = { show: v => v.toString() } }: Nested) {}
13+
14+
interface Tuples {
15+
prop: [string, number];
16+
}
17+
function g({ prop = ["hello", 1234] }: Tuples) {}
18+
19+
interface StringUnion {
20+
prop: "foo" | "bar";
21+
}
22+
function h({ prop = "foo" }: StringUnion) {}
23+
24+
interface StringIdentity {
25+
stringIdentity(s: string): string;
26+
}
27+
let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x};
28+
29+
30+
31+
32+
//// [contextuallyTypedBindingInitializer.js]
33+
function f(_a) {
34+
var _b = _a.show, show = _b === void 0 ? function (v) { return v.toString(); } : _b;
35+
}
36+
function f2(_a) {
37+
var _b = _a["show"], showRename = _b === void 0 ? function (v) { return v.toString(); } : _b;
38+
}
39+
function f3(_a) {
40+
var _b = "show", _c = _a[_b], showRename = _c === void 0 ? function (v) { return v.toString(); } : _c;
41+
}
42+
function ff(_a) {
43+
var _b = _a.nested, nested = _b === void 0 ? { show: function (v) { return v.toString(); } } : _b;
44+
}
45+
function g(_a) {
46+
var _b = _a.prop, prop = _b === void 0 ? ["hello", 1234] : _b;
47+
}
48+
function h(_a) {
49+
var _b = _a.prop, prop = _b === void 0 ? "foo" : _b;
50+
}
51+
var _a = { stringIdentity: function (x) { return x; } }.stringIdentity, id = _a === void 0 ? function (arg) { return arg; } : _a;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
=== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts ===
2+
interface Show {
3+
>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0))
4+
5+
show: (x: number) => string;
6+
>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 0, 16))
7+
>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 1, 11))
8+
}
9+
function f({ show = v => v.toString() }: Show) {}
10+
>f : Symbol(f, Decl(contextuallyTypedBindingInitializer.ts, 2, 1))
11+
>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 3, 12))
12+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 3, 19))
13+
>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
14+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 3, 19))
15+
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
16+
>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0))
17+
18+
function f2({ "show": showRename = v => v.toString() }: Show) {}
19+
>f2 : Symbol(f2, Decl(contextuallyTypedBindingInitializer.ts, 3, 49))
20+
>showRename : Symbol(showRename, Decl(contextuallyTypedBindingInitializer.ts, 4, 13))
21+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 4, 34))
22+
>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
23+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 4, 34))
24+
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
25+
>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0))
26+
27+
function f3({ ["show"]: showRename = v => v.toString() }: Show) {}
28+
>f3 : Symbol(f3, Decl(contextuallyTypedBindingInitializer.ts, 4, 64))
29+
>showRename : Symbol(showRename, Decl(contextuallyTypedBindingInitializer.ts, 5, 13))
30+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 5, 36))
31+
>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
32+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 5, 36))
33+
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
34+
>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0))
35+
36+
interface Nested {
37+
>Nested : Symbol(Nested, Decl(contextuallyTypedBindingInitializer.ts, 5, 66))
38+
39+
nested: Show
40+
>nested : Symbol(nested, Decl(contextuallyTypedBindingInitializer.ts, 7, 18))
41+
>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0))
42+
}
43+
function ff({ nested = { show: v => v.toString() } }: Nested) {}
44+
>ff : Symbol(ff, Decl(contextuallyTypedBindingInitializer.ts, 9, 1))
45+
>nested : Symbol(nested, Decl(contextuallyTypedBindingInitializer.ts, 10, 13))
46+
>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 10, 24))
47+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 10, 30))
48+
>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
49+
>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 10, 30))
50+
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
51+
>Nested : Symbol(Nested, Decl(contextuallyTypedBindingInitializer.ts, 5, 66))
52+
53+
interface Tuples {
54+
>Tuples : Symbol(Tuples, Decl(contextuallyTypedBindingInitializer.ts, 10, 64))
55+
56+
prop: [string, number];
57+
>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 12, 18))
58+
}
59+
function g({ prop = ["hello", 1234] }: Tuples) {}
60+
>g : Symbol(g, Decl(contextuallyTypedBindingInitializer.ts, 14, 1))
61+
>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 15, 12))
62+
>Tuples : Symbol(Tuples, Decl(contextuallyTypedBindingInitializer.ts, 10, 64))
63+
64+
interface StringUnion {
65+
>StringUnion : Symbol(StringUnion, Decl(contextuallyTypedBindingInitializer.ts, 15, 49))
66+
67+
prop: "foo" | "bar";
68+
>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 17, 23))
69+
}
70+
function h({ prop = "foo" }: StringUnion) {}
71+
>h : Symbol(h, Decl(contextuallyTypedBindingInitializer.ts, 19, 1))
72+
>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 20, 12))
73+
>StringUnion : Symbol(StringUnion, Decl(contextuallyTypedBindingInitializer.ts, 15, 49))
74+
75+
interface StringIdentity {
76+
>StringIdentity : Symbol(StringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 20, 44))
77+
78+
stringIdentity(s: string): string;
79+
>stringIdentity : Symbol(stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 22, 26))
80+
>s : Symbol(s, Decl(contextuallyTypedBindingInitializer.ts, 23, 19))
81+
}
82+
let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x};
83+
>stringIdentity : Symbol(StringIdentity.stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 22, 26))
84+
>id : Symbol(id, Decl(contextuallyTypedBindingInitializer.ts, 25, 5))
85+
>arg : Symbol(arg, Decl(contextuallyTypedBindingInitializer.ts, 25, 26))
86+
>arg : Symbol(arg, Decl(contextuallyTypedBindingInitializer.ts, 25, 26))
87+
>StringIdentity : Symbol(StringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 20, 44))
88+
>stringIdentity : Symbol(stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 25, 59))
89+
>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 25, 75))
90+
>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 25, 75))
91+
92+
93+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
=== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts ===
2+
interface Show {
3+
>Show : Show
4+
5+
show: (x: number) => string;
6+
>show : (x: number) => string
7+
>x : number
8+
}
9+
function f({ show = v => v.toString() }: Show) {}
10+
>f : ({show}: Show) => void
11+
>show : (x: number) => string
12+
>v => v.toString() : (v: number) => string
13+
>v : number
14+
>v.toString() : string
15+
>v.toString : (radix?: number) => string
16+
>v : number
17+
>toString : (radix?: number) => string
18+
>Show : Show
19+
20+
function f2({ "show": showRename = v => v.toString() }: Show) {}
21+
>f2 : ({"show": showRename}: Show) => void
22+
>showRename : (x: number) => string
23+
>v => v.toString() : (v: number) => string
24+
>v : number
25+
>v.toString() : string
26+
>v.toString : (radix?: number) => string
27+
>v : number
28+
>toString : (radix?: number) => string
29+
>Show : Show
30+
31+
function f3({ ["show"]: showRename = v => v.toString() }: Show) {}
32+
>f3 : ({["show"]: showRename}: Show) => void
33+
>"show" : string
34+
>showRename : (x: number) => string
35+
>v => v.toString() : (v: number) => string
36+
>v : number
37+
>v.toString() : string
38+
>v.toString : (radix?: number) => string
39+
>v : number
40+
>toString : (radix?: number) => string
41+
>Show : Show
42+
43+
interface Nested {
44+
>Nested : Nested
45+
46+
nested: Show
47+
>nested : Show
48+
>Show : Show
49+
}
50+
function ff({ nested = { show: v => v.toString() } }: Nested) {}
51+
>ff : ({nested}: Nested) => void
52+
>nested : Show
53+
>{ show: v => v.toString() } : { show: (v: number) => string; }
54+
>show : (v: number) => string
55+
>v => v.toString() : (v: number) => string
56+
>v : number
57+
>v.toString() : string
58+
>v.toString : (radix?: number) => string
59+
>v : number
60+
>toString : (radix?: number) => string
61+
>Nested : Nested
62+
63+
interface Tuples {
64+
>Tuples : Tuples
65+
66+
prop: [string, number];
67+
>prop : [string, number]
68+
}
69+
function g({ prop = ["hello", 1234] }: Tuples) {}
70+
>g : ({prop}: Tuples) => void
71+
>prop : [string, number]
72+
>["hello", 1234] : [string, number]
73+
>"hello" : string
74+
>1234 : number
75+
>Tuples : Tuples
76+
77+
interface StringUnion {
78+
>StringUnion : StringUnion
79+
80+
prop: "foo" | "bar";
81+
>prop : "foo" | "bar"
82+
}
83+
function h({ prop = "foo" }: StringUnion) {}
84+
>h : ({prop}: StringUnion) => void
85+
>prop : "foo" | "bar"
86+
>"foo" : "foo"
87+
>StringUnion : StringUnion
88+
89+
interface StringIdentity {
90+
>StringIdentity : StringIdentity
91+
92+
stringIdentity(s: string): string;
93+
>stringIdentity : (s: string) => string
94+
>s : string
95+
}
96+
let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x};
97+
>stringIdentity : any
98+
>id : (s: string) => string
99+
>arg => arg : (arg: string) => string
100+
>arg : string
101+
>arg : string
102+
>StringIdentity : StringIdentity
103+
>{ stringIdentity: x => x} : { stringIdentity: (x: string) => string; }
104+
>stringIdentity : (x: string) => string
105+
>x => x : (x: string) => string
106+
>x : string
107+
>x : string
108+
109+
110+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(4,20): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
2+
Type 'number' is not assignable to type 'string'.
3+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(5,23): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
4+
Type 'number' is not assignable to type 'string'.
5+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(6,25): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
6+
Type 'number' is not assignable to type 'string'.
7+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(11,23): error TS2322: Type '{ show: (v: number) => number; }' is not assignable to type 'Show'.
8+
Types of property 'show' are incompatible.
9+
Type '(v: number) => number' is not assignable to type '(x: number) => string'.
10+
Type 'number' is not assignable to type 'string'.
11+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(16,23): error TS2322: Type '(arg: string) => number' is not assignable to type '(s: string) => string'.
12+
Type 'number' is not assignable to type 'string'.
13+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(21,14): error TS2322: Type '[number, number]' is not assignable to type '[string, number]'.
14+
Types of property '0' are incompatible.
15+
Type 'number' is not assignable to type 'string'.
16+
tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(26,14): error TS2322: Type '"baz"' is not assignable to type '"foo" | "bar"'.
17+
Type '"baz"' is not assignable to type '"bar"'.
18+
19+
20+
==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts (7 errors) ====
21+
interface Show {
22+
show: (x: number) => string;
23+
}
24+
function f({ show: showRename = v => v }: Show) {}
25+
~~~~~~~~~~
26+
!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
27+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
28+
function f2({ "show": showRename = v => v }: Show) {}
29+
~~~~~~~~~~
30+
!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
31+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
32+
function f3({ ["show"]: showRename = v => v }: Show) {}
33+
~~~~~~~~~~
34+
!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
35+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
36+
37+
interface Nested {
38+
nested: Show
39+
}
40+
function ff({ nested: nestedRename = { show: v => v } }: Nested) {}
41+
~~~~~~~~~~~~
42+
!!! error TS2322: Type '{ show: (v: number) => number; }' is not assignable to type 'Show'.
43+
!!! error TS2322: Types of property 'show' are incompatible.
44+
!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'.
45+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
46+
47+
interface StringIdentity {
48+
stringIdentity(s: string): string;
49+
}
50+
let { stringIdentity: id = arg => arg.length }: StringIdentity = { stringIdentity: x => x};
51+
~~
52+
!!! error TS2322: Type '(arg: string) => number' is not assignable to type '(s: string) => string'.
53+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
54+
55+
interface Tuples {
56+
prop: [string, number];
57+
}
58+
function g({ prop = [101, 1234] }: Tuples) {}
59+
~~~~
60+
!!! error TS2322: Type '[number, number]' is not assignable to type '[string, number]'.
61+
!!! error TS2322: Types of property '0' are incompatible.
62+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
63+
64+
interface StringUnion {
65+
prop: "foo" | "bar";
66+
}
67+
function h({ prop = "baz" }: StringUnion) {}
68+
~~~~
69+
!!! error TS2322: Type '"baz"' is not assignable to type '"foo" | "bar"'.
70+
!!! error TS2322: Type '"baz"' is not assignable to type '"bar"'.
71+

0 commit comments

Comments
 (0)