Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2604,7 +2604,7 @@ namespace ts {
return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((<ComputedPropertyName>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 = <BindingPattern>declaration.parent;
const parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent);
Expand All @@ -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.
Expand Down Expand Up @@ -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 = <VariableLikeDeclaration>node.parent;
if (node === declaration.initializer) {
Expand All @@ -7845,6 +7851,18 @@ namespace ts {
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>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;
}
Expand Down
51 changes: 51 additions & 0 deletions tests/baselines/reference/contextuallyTypedBindingInitializer.js
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))



110 changes: 110 additions & 0 deletions tests/baselines/reference/contextuallyTypedBindingInitializer.types
Original file line number Diff line number Diff line change
@@ -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



Original file line number Diff line number Diff line change
@@ -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"'.

Loading