diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4926ee6fa08..692b6ea848b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2604,7 +2604,7 @@ namespace ts { return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((name).expression.kind); } - // Return the inferred type for a binding element + /** Return the inferred type for a binding element */ function getTypeForBindingElement(declaration: BindingElement): Type { const pattern = declaration.parent; const parentType = getTypeForBindingElementParent(pattern.parent); @@ -2630,6 +2630,9 @@ namespace ts { // computed properties with non-literal names are treated as 'any' return anyType; } + if (declaration.initializer) { + getContextualType(declaration.initializer); + } // Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature, // or otherwise the type of the string index signature. @@ -7825,11 +7828,14 @@ namespace ts { return undefined; } - // In a variable, parameter or property declaration with a type annotation, the contextual type of an initializer - // expression is the type of the variable, parameter or property. Otherwise, in a parameter declaration of a - // contextually typed function expression, the contextual type of an initializer expression is the contextual type - // of the parameter. Otherwise, in a variable or parameter declaration with a binding pattern name, the contextual - // type of an initializer expression is the type implied by the binding pattern. + // In a variable, parameter or property declaration with a type annotation, + // the contextual type of an initializer expression is the type of the variable, parameter or property. + // Otherwise, in a parameter declaration of a contextually typed function expression, + // the contextual type of an initializer expression is the contextual type of the parameter. + // Otherwise, in a variable or parameter declaration with a binding pattern name, + // the contextual type of an initializer expression is the type implied by the binding pattern. + // Otherwise, in a binding pattern inside a variable or parameter declaration, + // the contextual type of an initializer expression is the type annotation of the containing declaration, if present. function getContextualTypeForInitializerExpression(node: Expression): Type { const declaration = node.parent; if (node === declaration.initializer) { @@ -7845,6 +7851,18 @@ namespace ts { if (isBindingPattern(declaration.name)) { return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true); } + if (isBindingPattern(declaration.parent)) { + const parentDeclaration = declaration.parent.parent; + const name = declaration.propertyName || declaration.name; + if (isVariableLike(parentDeclaration) && + parentDeclaration.type && + !isBindingPattern(name)) { + const text = getTextOfPropertyName(name); + if (text) { + return getTypeOfPropertyOfType(getTypeFromTypeNode(parentDeclaration.type), text); + } + } + } } return undefined; } diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializer.js b/tests/baselines/reference/contextuallyTypedBindingInitializer.js new file mode 100644 index 00000000000..f2424ce55d7 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBindingInitializer.js @@ -0,0 +1,51 @@ +//// [contextuallyTypedBindingInitializer.ts] +interface Show { + show: (x: number) => string; +} +function f({ show = v => v.toString() }: Show) {} +function f2({ "show": showRename = v => v.toString() }: Show) {} +function f3({ ["show"]: showRename = v => v.toString() }: Show) {} + +interface Nested { + nested: Show +} +function ff({ nested = { show: v => v.toString() } }: Nested) {} + +interface Tuples { + prop: [string, number]; +} +function g({ prop = ["hello", 1234] }: Tuples) {} + +interface StringUnion { + prop: "foo" | "bar"; +} +function h({ prop = "foo" }: StringUnion) {} + +interface StringIdentity { + stringIdentity(s: string): string; +} +let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x}; + + + + +//// [contextuallyTypedBindingInitializer.js] +function f(_a) { + var _b = _a.show, show = _b === void 0 ? function (v) { return v.toString(); } : _b; +} +function f2(_a) { + var _b = _a["show"], showRename = _b === void 0 ? function (v) { return v.toString(); } : _b; +} +function f3(_a) { + var _b = "show", _c = _a[_b], showRename = _c === void 0 ? function (v) { return v.toString(); } : _c; +} +function ff(_a) { + var _b = _a.nested, nested = _b === void 0 ? { show: function (v) { return v.toString(); } } : _b; +} +function g(_a) { + var _b = _a.prop, prop = _b === void 0 ? ["hello", 1234] : _b; +} +function h(_a) { + var _b = _a.prop, prop = _b === void 0 ? "foo" : _b; +} +var _a = { stringIdentity: function (x) { return x; } }.stringIdentity, id = _a === void 0 ? function (arg) { return arg; } : _a; diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializer.symbols b/tests/baselines/reference/contextuallyTypedBindingInitializer.symbols new file mode 100644 index 00000000000..5530c958e91 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBindingInitializer.symbols @@ -0,0 +1,93 @@ +=== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts === +interface Show { +>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0)) + + show: (x: number) => string; +>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 0, 16)) +>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 1, 11)) +} +function f({ show = v => v.toString() }: Show) {} +>f : Symbol(f, Decl(contextuallyTypedBindingInitializer.ts, 2, 1)) +>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 3, 12)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 3, 19)) +>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 3, 19)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0)) + +function f2({ "show": showRename = v => v.toString() }: Show) {} +>f2 : Symbol(f2, Decl(contextuallyTypedBindingInitializer.ts, 3, 49)) +>showRename : Symbol(showRename, Decl(contextuallyTypedBindingInitializer.ts, 4, 13)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 4, 34)) +>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 4, 34)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0)) + +function f3({ ["show"]: showRename = v => v.toString() }: Show) {} +>f3 : Symbol(f3, Decl(contextuallyTypedBindingInitializer.ts, 4, 64)) +>showRename : Symbol(showRename, Decl(contextuallyTypedBindingInitializer.ts, 5, 13)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 5, 36)) +>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 5, 36)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0)) + +interface Nested { +>Nested : Symbol(Nested, Decl(contextuallyTypedBindingInitializer.ts, 5, 66)) + + nested: Show +>nested : Symbol(nested, Decl(contextuallyTypedBindingInitializer.ts, 7, 18)) +>Show : Symbol(Show, Decl(contextuallyTypedBindingInitializer.ts, 0, 0)) +} +function ff({ nested = { show: v => v.toString() } }: Nested) {} +>ff : Symbol(ff, Decl(contextuallyTypedBindingInitializer.ts, 9, 1)) +>nested : Symbol(nested, Decl(contextuallyTypedBindingInitializer.ts, 10, 13)) +>show : Symbol(show, Decl(contextuallyTypedBindingInitializer.ts, 10, 24)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 10, 30)) +>v.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>v : Symbol(v, Decl(contextuallyTypedBindingInitializer.ts, 10, 30)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) +>Nested : Symbol(Nested, Decl(contextuallyTypedBindingInitializer.ts, 5, 66)) + +interface Tuples { +>Tuples : Symbol(Tuples, Decl(contextuallyTypedBindingInitializer.ts, 10, 64)) + + prop: [string, number]; +>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 12, 18)) +} +function g({ prop = ["hello", 1234] }: Tuples) {} +>g : Symbol(g, Decl(contextuallyTypedBindingInitializer.ts, 14, 1)) +>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 15, 12)) +>Tuples : Symbol(Tuples, Decl(contextuallyTypedBindingInitializer.ts, 10, 64)) + +interface StringUnion { +>StringUnion : Symbol(StringUnion, Decl(contextuallyTypedBindingInitializer.ts, 15, 49)) + + prop: "foo" | "bar"; +>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 17, 23)) +} +function h({ prop = "foo" }: StringUnion) {} +>h : Symbol(h, Decl(contextuallyTypedBindingInitializer.ts, 19, 1)) +>prop : Symbol(prop, Decl(contextuallyTypedBindingInitializer.ts, 20, 12)) +>StringUnion : Symbol(StringUnion, Decl(contextuallyTypedBindingInitializer.ts, 15, 49)) + +interface StringIdentity { +>StringIdentity : Symbol(StringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 20, 44)) + + stringIdentity(s: string): string; +>stringIdentity : Symbol(stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 22, 26)) +>s : Symbol(s, Decl(contextuallyTypedBindingInitializer.ts, 23, 19)) +} +let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x}; +>stringIdentity : Symbol(StringIdentity.stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 22, 26)) +>id : Symbol(id, Decl(contextuallyTypedBindingInitializer.ts, 25, 5)) +>arg : Symbol(arg, Decl(contextuallyTypedBindingInitializer.ts, 25, 26)) +>arg : Symbol(arg, Decl(contextuallyTypedBindingInitializer.ts, 25, 26)) +>StringIdentity : Symbol(StringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 20, 44)) +>stringIdentity : Symbol(stringIdentity, Decl(contextuallyTypedBindingInitializer.ts, 25, 59)) +>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 25, 75)) +>x : Symbol(x, Decl(contextuallyTypedBindingInitializer.ts, 25, 75)) + + + diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializer.types b/tests/baselines/reference/contextuallyTypedBindingInitializer.types new file mode 100644 index 00000000000..0d4dfcdd5d9 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBindingInitializer.types @@ -0,0 +1,110 @@ +=== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts === +interface Show { +>Show : Show + + show: (x: number) => string; +>show : (x: number) => string +>x : number +} +function f({ show = v => v.toString() }: Show) {} +>f : ({show}: Show) => void +>show : (x: number) => string +>v => v.toString() : (v: number) => string +>v : number +>v.toString() : string +>v.toString : (radix?: number) => string +>v : number +>toString : (radix?: number) => string +>Show : Show + +function f2({ "show": showRename = v => v.toString() }: Show) {} +>f2 : ({"show": showRename}: Show) => void +>showRename : (x: number) => string +>v => v.toString() : (v: number) => string +>v : number +>v.toString() : string +>v.toString : (radix?: number) => string +>v : number +>toString : (radix?: number) => string +>Show : Show + +function f3({ ["show"]: showRename = v => v.toString() }: Show) {} +>f3 : ({["show"]: showRename}: Show) => void +>"show" : string +>showRename : (x: number) => string +>v => v.toString() : (v: number) => string +>v : number +>v.toString() : string +>v.toString : (radix?: number) => string +>v : number +>toString : (radix?: number) => string +>Show : Show + +interface Nested { +>Nested : Nested + + nested: Show +>nested : Show +>Show : Show +} +function ff({ nested = { show: v => v.toString() } }: Nested) {} +>ff : ({nested}: Nested) => void +>nested : Show +>{ show: v => v.toString() } : { show: (v: number) => string; } +>show : (v: number) => string +>v => v.toString() : (v: number) => string +>v : number +>v.toString() : string +>v.toString : (radix?: number) => string +>v : number +>toString : (radix?: number) => string +>Nested : Nested + +interface Tuples { +>Tuples : Tuples + + prop: [string, number]; +>prop : [string, number] +} +function g({ prop = ["hello", 1234] }: Tuples) {} +>g : ({prop}: Tuples) => void +>prop : [string, number] +>["hello", 1234] : [string, number] +>"hello" : string +>1234 : number +>Tuples : Tuples + +interface StringUnion { +>StringUnion : StringUnion + + prop: "foo" | "bar"; +>prop : "foo" | "bar" +} +function h({ prop = "foo" }: StringUnion) {} +>h : ({prop}: StringUnion) => void +>prop : "foo" | "bar" +>"foo" : "foo" +>StringUnion : StringUnion + +interface StringIdentity { +>StringIdentity : StringIdentity + + stringIdentity(s: string): string; +>stringIdentity : (s: string) => string +>s : string +} +let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x}; +>stringIdentity : any +>id : (s: string) => string +>arg => arg : (arg: string) => string +>arg : string +>arg : string +>StringIdentity : StringIdentity +>{ stringIdentity: x => x} : { stringIdentity: (x: string) => string; } +>stringIdentity : (x: string) => string +>x => x : (x: string) => string +>x : string +>x : string + + + diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt new file mode 100644 index 00000000000..689645386c4 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.errors.txt @@ -0,0 +1,71 @@ +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(4,20): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(5,23): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(6,25): error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(11,23): error TS2322: Type '{ show: (v: number) => number; }' is not assignable to type 'Show'. + Types of property 'show' are incompatible. + Type '(v: number) => number' is not assignable to type '(x: number) => string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(16,23): error TS2322: Type '(arg: string) => number' is not assignable to type '(s: string) => string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(21,14): error TS2322: Type '[number, number]' is not assignable to type '[string, number]'. + Types of property '0' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts(26,14): error TS2322: Type '"baz"' is not assignable to type '"foo" | "bar"'. + Type '"baz"' is not assignable to type '"bar"'. + + +==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts (7 errors) ==== + interface Show { + show: (x: number) => string; + } + function f({ show: showRename = v => v }: Show) {} + ~~~~~~~~~~ +!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + function f2({ "show": showRename = v => v }: Show) {} + ~~~~~~~~~~ +!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + function f3({ ["show"]: showRename = v => v }: Show) {} + ~~~~~~~~~~ +!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + interface Nested { + nested: Show + } + function ff({ nested: nestedRename = { show: v => v } }: Nested) {} + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ show: (v: number) => number; }' is not assignable to type 'Show'. +!!! error TS2322: Types of property 'show' are incompatible. +!!! error TS2322: Type '(v: number) => number' is not assignable to type '(x: number) => string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + interface StringIdentity { + stringIdentity(s: string): string; + } + let { stringIdentity: id = arg => arg.length }: StringIdentity = { stringIdentity: x => x}; + ~~ +!!! error TS2322: Type '(arg: string) => number' is not assignable to type '(s: string) => string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + interface Tuples { + prop: [string, number]; + } + function g({ prop = [101, 1234] }: Tuples) {} + ~~~~ +!!! error TS2322: Type '[number, number]' is not assignable to type '[string, number]'. +!!! error TS2322: Types of property '0' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + interface StringUnion { + prop: "foo" | "bar"; + } + function h({ prop = "baz" }: StringUnion) {} + ~~~~ +!!! error TS2322: Type '"baz"' is not assignable to type '"foo" | "bar"'. +!!! error TS2322: Type '"baz"' is not assignable to type '"bar"'. + \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.js b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.js new file mode 100644 index 00000000000..3f1ed156fdb --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedBindingInitializerNegative.js @@ -0,0 +1,49 @@ +//// [contextuallyTypedBindingInitializerNegative.ts] +interface Show { + show: (x: number) => string; +} +function f({ show: showRename = v => v }: Show) {} +function f2({ "show": showRename = v => v }: Show) {} +function f3({ ["show"]: showRename = v => v }: Show) {} + +interface Nested { + nested: Show +} +function ff({ nested: nestedRename = { show: v => v } }: Nested) {} + +interface StringIdentity { + stringIdentity(s: string): string; +} +let { stringIdentity: id = arg => arg.length }: StringIdentity = { stringIdentity: x => x}; + +interface Tuples { + prop: [string, number]; +} +function g({ prop = [101, 1234] }: Tuples) {} + +interface StringUnion { + prop: "foo" | "bar"; +} +function h({ prop = "baz" }: StringUnion) {} + + +//// [contextuallyTypedBindingInitializerNegative.js] +function f(_a) { + var _b = _a.show, showRename = _b === void 0 ? function (v) { return v; } : _b; +} +function f2(_a) { + var _b = _a["show"], showRename = _b === void 0 ? function (v) { return v; } : _b; +} +function f3(_a) { + var _b = "show", _c = _a[_b], showRename = _c === void 0 ? function (v) { return v; } : _c; +} +function ff(_a) { + var _b = _a.nested, nestedRename = _b === void 0 ? { show: function (v) { return v; } } : _b; +} +var _a = { stringIdentity: function (x) { return x; } }.stringIdentity, id = _a === void 0 ? function (arg) { return arg.length; } : _a; +function g(_a) { + var _b = _a.prop, prop = _b === void 0 ? [101, 1234] : _b; +} +function h(_a) { + var _b = _a.prop, prop = _b === void 0 ? "baz" : _b; +} diff --git a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts new file mode 100644 index 00000000000..c02aa5b63e8 --- /dev/null +++ b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializer.ts @@ -0,0 +1,29 @@ +// @noImplicitAny: true +interface Show { + show: (x: number) => string; +} +function f({ show = v => v.toString() }: Show) {} +function f2({ "show": showRename = v => v.toString() }: Show) {} +function f3({ ["show"]: showRename = v => v.toString() }: Show) {} + +interface Nested { + nested: Show +} +function ff({ nested = { show: v => v.toString() } }: Nested) {} + +interface Tuples { + prop: [string, number]; +} +function g({ prop = ["hello", 1234] }: Tuples) {} + +interface StringUnion { + prop: "foo" | "bar"; +} +function h({ prop = "foo" }: StringUnion) {} + +interface StringIdentity { + stringIdentity(s: string): string; +} +let { stringIdentity: id = arg => arg }: StringIdentity = { stringIdentity: x => x}; + + diff --git a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts new file mode 100644 index 00000000000..fb71da7b655 --- /dev/null +++ b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedBindingInitializerNegative.ts @@ -0,0 +1,27 @@ +// @noImplicitAny: true +interface Show { + show: (x: number) => string; +} +function f({ show: showRename = v => v }: Show) {} +function f2({ "show": showRename = v => v }: Show) {} +function f3({ ["show"]: showRename = v => v }: Show) {} + +interface Nested { + nested: Show +} +function ff({ nested: nestedRename = { show: v => v } }: Nested) {} + +interface StringIdentity { + stringIdentity(s: string): string; +} +let { stringIdentity: id = arg => arg.length }: StringIdentity = { stringIdentity: x => x}; + +interface Tuples { + prop: [string, number]; +} +function g({ prop = [101, 1234] }: Tuples) {} + +interface StringUnion { + prop: "foo" | "bar"; +} +function h({ prop = "baz" }: StringUnion) {}