Skip to content

Commit 38da682

Browse files
authored
Unify contextual signature type parameter assignment (#31574)
* Unify conditional signature type assignment * Moonomorphism
1 parent 5c21fad commit 38da682

6 files changed

+313
-7
lines changed

src/compiler/checker.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -5231,7 +5231,7 @@ namespace ts {
52315231
}
52325232
}
52335233
// Use contextual parameter type if one is available
5234-
const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration);
5234+
const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration, /*forCache*/ true);
52355235
if (type) {
52365236
return addOptionality(type, isOptional);
52375237
}
@@ -11522,6 +11522,7 @@ namespace ts {
1152211522
case SyntaxKind.FunctionExpression:
1152311523
case SyntaxKind.ArrowFunction:
1152411524
case SyntaxKind.MethodDeclaration:
11525+
case SyntaxKind.FunctionDeclaration: // Function declarations can have context when annotated with a jsdoc @type
1152511526
return isContextSensitiveFunctionLikeDeclaration(<FunctionExpression | ArrowFunction | MethodDeclaration>node);
1152611527
case SyntaxKind.ObjectLiteralExpression:
1152711528
return some((<ObjectLiteralExpression>node).properties, isContextSensitive);
@@ -11555,6 +11556,9 @@ namespace ts {
1155511556
}
1155611557

1155711558
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
11559+
if (isFunctionDeclaration(node) && (!isInJSFile(node) || !getTypeForDeclarationFromJSDocComment(node))) {
11560+
return false;
11561+
}
1155811562
// Functions with type parameters are not context sensitive.
1155911563
if (node.typeParameters) {
1156011564
return false;
@@ -18264,7 +18268,7 @@ namespace ts {
1826418268
}
1826518269

1826618270
// Return contextual type of parameter or undefined if no contextual type is available
18267-
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type | undefined {
18271+
function getContextuallyTypedParameterType(parameter: ParameterDeclaration, forCache: boolean): Type | undefined {
1826818272
const func = parameter.parent;
1826918273
if (!isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
1827018274
return undefined;
@@ -18285,8 +18289,21 @@ namespace ts {
1828518289
links.resolvedSignature = cached;
1828618290
return type;
1828718291
}
18288-
const contextualSignature = getContextualSignature(func);
18292+
let contextualSignature = getContextualSignature(func);
1828918293
if (contextualSignature) {
18294+
if (forCache) {
18295+
// Calling the below guarantees the types are primed and assigned in the same way
18296+
// as when the parameter is reached via `checkFunctionExpressionOrObjectLiteralMethod`.
18297+
// This should prevent any uninstantiated inference variables in the contextual signature
18298+
// from leaking, and should lock in cached parameter types via `assignContextualParameterTypes`
18299+
// which we will then immediately use the results of below.
18300+
contextuallyCheckFunctionExpressionOrObjectLiteralMethod(func);
18301+
const type = getTypeOfSymbol(getMergedSymbol(func.symbol));
18302+
if (isTypeAny(type)) {
18303+
return type;
18304+
}
18305+
contextualSignature = getSignaturesOfType(type, SignatureKind.Call)[0];
18306+
}
1829018307
const index = func.parameters.indexOf(parameter) - (getThisParameter(func) ? 1 : 0);
1829118308
return parameter.dotDotDotToken && lastOrUndefined(func.parameters) === parameter ?
1829218309
getRestTypeAtPosition(contextualSignature, index) :
@@ -18301,7 +18318,7 @@ namespace ts {
1830118318
}
1830218319
switch (declaration.kind) {
1830318320
case SyntaxKind.Parameter:
18304-
return getContextuallyTypedParameterType(declaration);
18321+
return getContextuallyTypedParameterType(declaration, /*forCache*/ false);
1830518322
case SyntaxKind.BindingElement:
1830618323
return getContextualTypeForBindingElement(declaration);
1830718324
// By default, do nothing and return undefined - only parameters and binding elements have context implied by a parent
@@ -23079,12 +23096,18 @@ namespace ts {
2307923096
checkGrammarForGenerator(node);
2308023097
}
2308123098

23082-
const links = getNodeLinks(node);
2308323099
const type = getTypeOfSymbol(getMergedSymbol(node.symbol));
2308423100
if (isTypeAny(type)) {
2308523101
return type;
2308623102
}
2308723103

23104+
contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode);
23105+
23106+
return type;
23107+
}
23108+
23109+
function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode?: CheckMode) {
23110+
const links = getNodeLinks(node);
2308823111
// Check if function expression is contextually typed and assign parameter types if so.
2308923112
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
2309023113
const contextualSignature = getContextualSignature(node);
@@ -23094,6 +23117,10 @@ namespace ts {
2309423117
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
2309523118
links.flags |= NodeCheckFlags.ContextChecked;
2309623119
if (contextualSignature) {
23120+
const type = getTypeOfSymbol(getMergedSymbol(node.symbol));
23121+
if (isTypeAny(type)) {
23122+
return;
23123+
}
2309723124
const signature = getSignaturesOfType(type, SignatureKind.Call)[0];
2309823125
if (isContextSensitive(node)) {
2309923126
const inferenceContext = getInferenceContext(node);
@@ -23114,8 +23141,6 @@ namespace ts {
2311423141
checkSignatureDeclaration(node);
2311523142
}
2311623143
}
23117-
23118-
return type;
2311923144
}
2312023145

2312123146
function getReturnOrPromisedType(node: FunctionLikeDeclaration | MethodSignature, functionFlags: FunctionFlags) {

tests/baselines/reference/fallbackToBindingPatternForTypeInference.symbols

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ trans(([b,c]) => 'foo');
1818

1919
trans(({d: [e,f]}) => 'foo');
2020
>trans : Symbol(trans, Decl(fallbackToBindingPatternForTypeInference.ts, 0, 0))
21+
>d : Symbol(d)
2122
>e : Symbol(e, Decl(fallbackToBindingPatternForTypeInference.ts, 3, 12))
2223
>f : Symbol(f, Decl(fallbackToBindingPatternForTypeInference.ts, 3, 14))
2324

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts]
2+
class ClassA<TEntityClass> {
3+
constructor(private entity?: TEntityClass, public settings?: SettingsInterface<TEntityClass>) {
4+
5+
}
6+
}
7+
export interface ValueInterface<TValueClass> {
8+
func?: (row: TValueClass) => any;
9+
value?: string;
10+
}
11+
export interface SettingsInterface<TClass> {
12+
values?: (row: TClass) => ValueInterface<TClass>[],
13+
}
14+
class ConcreteClass {
15+
theName = 'myClass';
16+
}
17+
18+
var thisGetsTheFalseError = new ClassA(new ConcreteClass(), {
19+
values: o => [
20+
{
21+
value: o.theName,
22+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
23+
}
24+
]
25+
});
26+
27+
var thisIsOk = new ClassA<ConcreteClass>(new ConcreteClass(), {
28+
values: o => [
29+
{
30+
value: o.theName,
31+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
32+
}
33+
]
34+
});
35+
36+
//// [inferenceDoesntCompareAgainstUninstantiatedTypeParameter.js]
37+
"use strict";
38+
exports.__esModule = true;
39+
var ClassA = /** @class */ (function () {
40+
function ClassA(entity, settings) {
41+
this.entity = entity;
42+
this.settings = settings;
43+
}
44+
return ClassA;
45+
}());
46+
var ConcreteClass = /** @class */ (function () {
47+
function ConcreteClass() {
48+
this.theName = 'myClass';
49+
}
50+
return ConcreteClass;
51+
}());
52+
var thisGetsTheFalseError = new ClassA(new ConcreteClass(), {
53+
values: function (o) { return [
54+
{
55+
value: o.theName,
56+
func: function (x) { return 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'; }
57+
}
58+
]; }
59+
});
60+
var thisIsOk = new ClassA(new ConcreteClass(), {
61+
values: function (o) { return [
62+
{
63+
value: o.theName,
64+
func: function (x) { return 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'; }
65+
}
66+
]; }
67+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
=== tests/cases/compiler/inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts ===
2+
class ClassA<TEntityClass> {
3+
>ClassA : Symbol(ClassA, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 0))
4+
>TEntityClass : Symbol(TEntityClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 13))
5+
6+
constructor(private entity?: TEntityClass, public settings?: SettingsInterface<TEntityClass>) {
7+
>entity : Symbol(ClassA.entity, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 1, 16))
8+
>TEntityClass : Symbol(TEntityClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 13))
9+
>settings : Symbol(ClassA.settings, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 1, 46))
10+
>SettingsInterface : Symbol(SettingsInterface, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 8, 1))
11+
>TEntityClass : Symbol(TEntityClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 13))
12+
13+
}
14+
}
15+
export interface ValueInterface<TValueClass> {
16+
>ValueInterface : Symbol(ValueInterface, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 4, 1))
17+
>TValueClass : Symbol(TValueClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 5, 32))
18+
19+
func?: (row: TValueClass) => any;
20+
>func : Symbol(ValueInterface.func, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 5, 46))
21+
>row : Symbol(row, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 6, 12))
22+
>TValueClass : Symbol(TValueClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 5, 32))
23+
24+
value?: string;
25+
>value : Symbol(ValueInterface.value, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 6, 37))
26+
}
27+
export interface SettingsInterface<TClass> {
28+
>SettingsInterface : Symbol(SettingsInterface, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 8, 1))
29+
>TClass : Symbol(TClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 9, 35))
30+
31+
values?: (row: TClass) => ValueInterface<TClass>[],
32+
>values : Symbol(SettingsInterface.values, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 9, 44))
33+
>row : Symbol(row, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 10, 14))
34+
>TClass : Symbol(TClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 9, 35))
35+
>ValueInterface : Symbol(ValueInterface, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 4, 1))
36+
>TClass : Symbol(TClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 9, 35))
37+
}
38+
class ConcreteClass {
39+
>ConcreteClass : Symbol(ConcreteClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 11, 1))
40+
41+
theName = 'myClass';
42+
>theName : Symbol(ConcreteClass.theName, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 12, 21))
43+
}
44+
45+
var thisGetsTheFalseError = new ClassA(new ConcreteClass(), {
46+
>thisGetsTheFalseError : Symbol(thisGetsTheFalseError, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 16, 3))
47+
>ClassA : Symbol(ClassA, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 0))
48+
>ConcreteClass : Symbol(ConcreteClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 11, 1))
49+
50+
values: o => [
51+
>values : Symbol(values, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 16, 61))
52+
>o : Symbol(o, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 17, 11))
53+
{
54+
value: o.theName,
55+
>value : Symbol(value, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 18, 9))
56+
>o.theName : Symbol(ConcreteClass.theName, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 12, 21))
57+
>o : Symbol(o, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 17, 11))
58+
>theName : Symbol(ConcreteClass.theName, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 12, 21))
59+
60+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
61+
>func : Symbol(func, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 19, 29))
62+
>x : Symbol(x, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 20, 17))
63+
}
64+
]
65+
});
66+
67+
var thisIsOk = new ClassA<ConcreteClass>(new ConcreteClass(), {
68+
>thisIsOk : Symbol(thisIsOk, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 25, 3))
69+
>ClassA : Symbol(ClassA, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 0, 0))
70+
>ConcreteClass : Symbol(ConcreteClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 11, 1))
71+
>ConcreteClass : Symbol(ConcreteClass, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 11, 1))
72+
73+
values: o => [
74+
>values : Symbol(values, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 25, 63))
75+
>o : Symbol(o, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 26, 11))
76+
{
77+
value: o.theName,
78+
>value : Symbol(value, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 27, 9))
79+
>o.theName : Symbol(ConcreteClass.theName, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 12, 21))
80+
>o : Symbol(o, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 26, 11))
81+
>theName : Symbol(ConcreteClass.theName, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 12, 21))
82+
83+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
84+
>func : Symbol(func, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 28, 29))
85+
>x : Symbol(x, Decl(inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts, 29, 17))
86+
}
87+
]
88+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/inferenceDoesntCompareAgainstUninstantiatedTypeParameter.ts ===
2+
class ClassA<TEntityClass> {
3+
>ClassA : ClassA<TEntityClass>
4+
5+
constructor(private entity?: TEntityClass, public settings?: SettingsInterface<TEntityClass>) {
6+
>entity : TEntityClass
7+
>settings : SettingsInterface<TEntityClass>
8+
9+
}
10+
}
11+
export interface ValueInterface<TValueClass> {
12+
func?: (row: TValueClass) => any;
13+
>func : (row: TValueClass) => any
14+
>row : TValueClass
15+
16+
value?: string;
17+
>value : string
18+
}
19+
export interface SettingsInterface<TClass> {
20+
values?: (row: TClass) => ValueInterface<TClass>[],
21+
>values : (row: TClass) => ValueInterface<TClass>[]
22+
>row : TClass
23+
}
24+
class ConcreteClass {
25+
>ConcreteClass : ConcreteClass
26+
27+
theName = 'myClass';
28+
>theName : string
29+
>'myClass' : "myClass"
30+
}
31+
32+
var thisGetsTheFalseError = new ClassA(new ConcreteClass(), {
33+
>thisGetsTheFalseError : ClassA<ConcreteClass>
34+
>new ClassA(new ConcreteClass(), { values: o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ]}) : ClassA<ConcreteClass>
35+
>ClassA : typeof ClassA
36+
>new ConcreteClass() : ConcreteClass
37+
>ConcreteClass : typeof ConcreteClass
38+
>{ values: o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ]} : { values: (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]; }
39+
40+
values: o => [
41+
>values : (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]
42+
>o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ] : (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]
43+
>o : ConcreteClass
44+
>[ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ] : { value: string; func: (x: ConcreteClass) => string; }[]
45+
{
46+
>{ value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } : { value: string; func: (x: ConcreteClass) => string; }
47+
48+
value: o.theName,
49+
>value : string
50+
>o.theName : string
51+
>o : ConcreteClass
52+
>theName : string
53+
54+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
55+
>func : (x: ConcreteClass) => string
56+
>x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' : (x: ConcreteClass) => string
57+
>x : ConcreteClass
58+
>'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' : "asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj"
59+
}
60+
]
61+
});
62+
63+
var thisIsOk = new ClassA<ConcreteClass>(new ConcreteClass(), {
64+
>thisIsOk : ClassA<ConcreteClass>
65+
>new ClassA<ConcreteClass>(new ConcreteClass(), { values: o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ]}) : ClassA<ConcreteClass>
66+
>ClassA : typeof ClassA
67+
>new ConcreteClass() : ConcreteClass
68+
>ConcreteClass : typeof ConcreteClass
69+
>{ values: o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ]} : { values: (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]; }
70+
71+
values: o => [
72+
>values : (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]
73+
>o => [ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ] : (o: ConcreteClass) => { value: string; func: (x: ConcreteClass) => string; }[]
74+
>o : ConcreteClass
75+
>[ { value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } ] : { value: string; func: (x: ConcreteClass) => string; }[]
76+
{
77+
>{ value: o.theName, func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' } : { value: string; func: (x: ConcreteClass) => string; }
78+
79+
value: o.theName,
80+
>value : string
81+
>o.theName : string
82+
>o : ConcreteClass
83+
>theName : string
84+
85+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
86+
>func : (x: ConcreteClass) => string
87+
>x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' : (x: ConcreteClass) => string
88+
>x : ConcreteClass
89+
>'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj' : "asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj"
90+
}
91+
]
92+
});

0 commit comments

Comments
 (0)