-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fix inference for contextually typed parameters with initializers #29576
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
Changes from 4 commits
8f5ef10
318678a
1bc4389
58e39ea
22d46ac
c6ccc5b
69d1048
25ac1ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts(8,27): error TS7022: '{ foo = 42 }' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. | ||
tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts(8,29): error TS7031: Binding element 'foo' implicitly has an 'any' type. | ||
tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts(14,27): error TS7006: Parameter 'foo' implicitly has an 'any' type. | ||
tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts(27,40): error TS7022: '{ foo = 42 }' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. | ||
tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts(27,42): error TS7031: Binding element 'foo' implicitly has an 'any' type. | ||
|
||
|
||
==== tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts (5 errors) ==== | ||
declare function id1<T>(input: T): T; | ||
declare function id2<T extends (x: any) => any>(input: T): T; | ||
declare function id3<T extends (x: { foo: any }) => any>(input: T): T; | ||
declare function id4<T extends (x: { foo?: number }) => any>(input: T): T; | ||
declare function id5<T extends (x?: number) => any>(input: T): T; | ||
|
||
const f10 = function ({ foo = 42 }) { return foo }; | ||
const f11 = id1(function ({ foo = 42 }) { return foo }); // Implicit any error | ||
~~~~~~~~~~~~ | ||
!!! error TS7022: '{ foo = 42 }' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this binding pattern circularly reference itself? o.O There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, strange. This error is caused by declaration file emit, it only appears with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we generate the error while looking up the type of the expression for emitting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The circularity error is caused by the parameter having itself as its contextual type. It's actually unrelated to this PR. For example, this causes the same error on declare function id<T>(input: T): T;
const f1 = id(function ({ foo }) { return foo }); |
||
~~~ | ||
!!! error TS7031: Binding element 'foo' implicitly has an 'any' type. | ||
const f12 = id2(function ({ foo = 42 }) { return foo }); | ||
const f13 = id3(function ({ foo = 42 }) { return foo }); | ||
const f14 = id4(function ({ foo = 42 }) { return foo }); | ||
|
||
const f20 = function (foo = 42) { return foo }; | ||
const f21 = id1(function (foo = 42) { return foo }); // Implicit any error | ||
~~~~~~~~ | ||
!!! error TS7006: Parameter 'foo' implicitly has an 'any' type. | ||
const f22 = id2(function (foo = 42) { return foo }); | ||
const f25 = id5(function (foo = 42) { return foo }); | ||
|
||
// Repro from #28816 | ||
|
||
function id<T>(input: T): T { return input } | ||
|
||
function getFoo ({ foo = 42 }) { | ||
return foo; | ||
} | ||
|
||
const newGetFoo = id(getFoo); | ||
const newGetFoo2 = id(function getFoo ({ foo = 42 }) { | ||
~~~~~~~~~~~~ | ||
!!! error TS7022: '{ foo = 42 }' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. | ||
~~~ | ||
!!! error TS7031: Binding element 'foo' implicitly has an 'any' type. | ||
return foo; | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
//// [contextuallyTypedParametersWithInitializers.ts] | ||
declare function id1<T>(input: T): T; | ||
declare function id2<T extends (x: any) => any>(input: T): T; | ||
declare function id3<T extends (x: { foo: any }) => any>(input: T): T; | ||
declare function id4<T extends (x: { foo?: number }) => any>(input: T): T; | ||
declare function id5<T extends (x?: number) => any>(input: T): T; | ||
|
||
const f10 = function ({ foo = 42 }) { return foo }; | ||
const f11 = id1(function ({ foo = 42 }) { return foo }); // Implicit any error | ||
const f12 = id2(function ({ foo = 42 }) { return foo }); | ||
const f13 = id3(function ({ foo = 42 }) { return foo }); | ||
const f14 = id4(function ({ foo = 42 }) { return foo }); | ||
|
||
const f20 = function (foo = 42) { return foo }; | ||
const f21 = id1(function (foo = 42) { return foo }); // Implicit any error | ||
const f22 = id2(function (foo = 42) { return foo }); | ||
const f25 = id5(function (foo = 42) { return foo }); | ||
|
||
// Repro from #28816 | ||
|
||
function id<T>(input: T): T { return input } | ||
|
||
function getFoo ({ foo = 42 }) { | ||
return foo; | ||
} | ||
|
||
const newGetFoo = id(getFoo); | ||
const newGetFoo2 = id(function getFoo ({ foo = 42 }) { | ||
return foo; | ||
}); | ||
|
||
|
||
//// [contextuallyTypedParametersWithInitializers.js] | ||
"use strict"; | ||
var f10 = function (_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}; | ||
var f11 = id1(function (_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}); // Implicit any error | ||
var f12 = id2(function (_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}); | ||
var f13 = id3(function (_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}); | ||
var f14 = id4(function (_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}); | ||
var f20 = function (foo) { | ||
if (foo === void 0) { foo = 42; } | ||
return foo; | ||
}; | ||
var f21 = id1(function (foo) { | ||
if (foo === void 0) { foo = 42; } | ||
return foo; | ||
}); // Implicit any error | ||
var f22 = id2(function (foo) { | ||
if (foo === void 0) { foo = 42; } | ||
return foo; | ||
}); | ||
var f25 = id5(function (foo) { | ||
if (foo === void 0) { foo = 42; } | ||
return foo; | ||
}); | ||
// Repro from #28816 | ||
function id(input) { return input; } | ||
function getFoo(_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
} | ||
var newGetFoo = id(getFoo); | ||
var newGetFoo2 = id(function getFoo(_a) { | ||
var _b = _a.foo, foo = _b === void 0 ? 42 : _b; | ||
return foo; | ||
}); | ||
|
||
|
||
//// [contextuallyTypedParametersWithInitializers.d.ts] | ||
declare function id1<T>(input: T): T; | ||
declare function id2<T extends (x: any) => any>(input: T): T; | ||
declare function id3<T extends (x: { | ||
foo: any; | ||
}) => any>(input: T): T; | ||
declare function id4<T extends (x: { | ||
foo?: number; | ||
}) => any>(input: T): T; | ||
declare function id5<T extends (x?: number) => any>(input: T): T; | ||
declare const f10: ({ foo }: { | ||
foo?: number | undefined; | ||
}) => number; | ||
declare const f11: ({ foo }: any) => any; | ||
declare const f12: ({ foo }: any) => any; | ||
declare const f13: ({ foo }: { | ||
foo: any; | ||
}) => any; | ||
declare const f14: ({ foo }: { | ||
foo?: number | undefined; | ||
}) => number; | ||
declare const f20: (foo?: number) => number; | ||
declare const f21: (foo?: any) => any; | ||
declare const f22: (foo?: any) => any; | ||
declare const f25: (foo?: number | undefined) => number; | ||
declare function id<T>(input: T): T; | ||
declare function getFoo({ foo }: { | ||
foo?: number | undefined; | ||
}): number; | ||
declare const newGetFoo: typeof getFoo; | ||
declare const newGetFoo2: ({ foo }: any) => any; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
=== tests/cases/compiler/contextuallyTypedParametersWithInitializers.ts === | ||
declare function id1<T>(input: T): T; | ||
>id1 : Symbol(id1, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 0)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 21)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 24)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 21)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 21)) | ||
|
||
declare function id2<T extends (x: any) => any>(input: T): T; | ||
>id2 : Symbol(id2, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 37)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 21)) | ||
>x : Symbol(x, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 32)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 48)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 21)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 21)) | ||
|
||
declare function id3<T extends (x: { foo: any }) => any>(input: T): T; | ||
>id3 : Symbol(id3, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 61)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 21)) | ||
>x : Symbol(x, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 32)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 36)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 57)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 21)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 21)) | ||
|
||
declare function id4<T extends (x: { foo?: number }) => any>(input: T): T; | ||
>id4 : Symbol(id4, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 70)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 21)) | ||
>x : Symbol(x, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 32)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 36)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 61)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 21)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 21)) | ||
|
||
declare function id5<T extends (x?: number) => any>(input: T): T; | ||
>id5 : Symbol(id5, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 74)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 4, 21)) | ||
>x : Symbol(x, Decl(contextuallyTypedParametersWithInitializers.ts, 4, 32)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 4, 52)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 4, 21)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 4, 21)) | ||
|
||
const f10 = function ({ foo = 42 }) { return foo }; | ||
>f10 : Symbol(f10, Decl(contextuallyTypedParametersWithInitializers.ts, 6, 5)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 6, 23)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 6, 23)) | ||
|
||
const f11 = id1(function ({ foo = 42 }) { return foo }); // Implicit any error | ||
>f11 : Symbol(f11, Decl(contextuallyTypedParametersWithInitializers.ts, 7, 5)) | ||
>id1 : Symbol(id1, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 0)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 7, 27)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 7, 27)) | ||
|
||
const f12 = id2(function ({ foo = 42 }) { return foo }); | ||
>f12 : Symbol(f12, Decl(contextuallyTypedParametersWithInitializers.ts, 8, 5)) | ||
>id2 : Symbol(id2, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 37)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 8, 27)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 8, 27)) | ||
|
||
const f13 = id3(function ({ foo = 42 }) { return foo }); | ||
>f13 : Symbol(f13, Decl(contextuallyTypedParametersWithInitializers.ts, 9, 5)) | ||
>id3 : Symbol(id3, Decl(contextuallyTypedParametersWithInitializers.ts, 1, 61)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 9, 27)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 9, 27)) | ||
|
||
const f14 = id4(function ({ foo = 42 }) { return foo }); | ||
>f14 : Symbol(f14, Decl(contextuallyTypedParametersWithInitializers.ts, 10, 5)) | ||
>id4 : Symbol(id4, Decl(contextuallyTypedParametersWithInitializers.ts, 2, 70)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 10, 27)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 10, 27)) | ||
|
||
const f20 = function (foo = 42) { return foo }; | ||
>f20 : Symbol(f20, Decl(contextuallyTypedParametersWithInitializers.ts, 12, 5)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 12, 22)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 12, 22)) | ||
|
||
const f21 = id1(function (foo = 42) { return foo }); // Implicit any error | ||
>f21 : Symbol(f21, Decl(contextuallyTypedParametersWithInitializers.ts, 13, 5)) | ||
>id1 : Symbol(id1, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 0)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 13, 26)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 13, 26)) | ||
|
||
const f22 = id2(function (foo = 42) { return foo }); | ||
>f22 : Symbol(f22, Decl(contextuallyTypedParametersWithInitializers.ts, 14, 5)) | ||
>id2 : Symbol(id2, Decl(contextuallyTypedParametersWithInitializers.ts, 0, 37)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 14, 26)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 14, 26)) | ||
|
||
const f25 = id5(function (foo = 42) { return foo }); | ||
>f25 : Symbol(f25, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 5)) | ||
>id5 : Symbol(id5, Decl(contextuallyTypedParametersWithInitializers.ts, 3, 74)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 26)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 26)) | ||
|
||
// Repro from #28816 | ||
|
||
function id<T>(input: T): T { return input } | ||
>id : Symbol(id, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 52)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 12)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 15)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 12)) | ||
>T : Symbol(T, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 12)) | ||
>input : Symbol(input, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 15)) | ||
|
||
function getFoo ({ foo = 42 }) { | ||
>getFoo : Symbol(getFoo, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 44)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 21, 18)) | ||
|
||
return foo; | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 21, 18)) | ||
} | ||
|
||
const newGetFoo = id(getFoo); | ||
>newGetFoo : Symbol(newGetFoo, Decl(contextuallyTypedParametersWithInitializers.ts, 25, 5)) | ||
>id : Symbol(id, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 52)) | ||
>getFoo : Symbol(getFoo, Decl(contextuallyTypedParametersWithInitializers.ts, 19, 44)) | ||
|
||
const newGetFoo2 = id(function getFoo ({ foo = 42 }) { | ||
>newGetFoo2 : Symbol(newGetFoo2, Decl(contextuallyTypedParametersWithInitializers.ts, 26, 5)) | ||
>id : Symbol(id, Decl(contextuallyTypedParametersWithInitializers.ts, 15, 52)) | ||
>getFoo : Symbol(getFoo, Decl(contextuallyTypedParametersWithInitializers.ts, 26, 22)) | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 26, 40)) | ||
|
||
return foo; | ||
>foo : Symbol(foo, Decl(contextuallyTypedParametersWithInitializers.ts, 26, 40)) | ||
|
||
}); | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we rather write
declaration.initializer && !(declaration.kind === SyntaxKind.Parameter && getContextualType(<Expression>declaration.parent))
so we only callgetContextualType
if we absolutely need to?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, since it's used below, write
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, possibly. The value is precomputed because I use it in two locations. But I supposed I could turn it into a function that we only call when appropriate.