Skip to content

Commit 24b37e7

Browse files
committed
Unify conditional signature type assignment
1 parent 8ab0a25 commit 24b37e7

6 files changed

+312
-6
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5227,7 +5227,7 @@ namespace ts {
52275227
}
52285228
}
52295229
// Use contextual parameter type if one is available
5230-
const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration);
5230+
const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration, /*forCache*/ true);
52315231
if (type) {
52325232
return addOptionality(type, isOptional);
52335233
}
@@ -11484,6 +11484,7 @@ namespace ts {
1148411484
case SyntaxKind.FunctionExpression:
1148511485
case SyntaxKind.ArrowFunction:
1148611486
case SyntaxKind.MethodDeclaration:
11487+
case SyntaxKind.FunctionDeclaration: // Function declarations can have context when annotated with a jsdoc @type
1148711488
return isContextSensitiveFunctionLikeDeclaration(<FunctionExpression | ArrowFunction | MethodDeclaration>node);
1148811489
case SyntaxKind.ObjectLiteralExpression:
1148911490
return some((<ObjectLiteralExpression>node).properties, isContextSensitive);
@@ -11517,6 +11518,9 @@ namespace ts {
1151711518
}
1151811519

1151911520
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
11521+
if (isFunctionDeclaration(node) && (!isInJSFile(node) || !getTypeForDeclarationFromJSDocComment(node))) {
11522+
return false;
11523+
}
1152011524
// Functions with type parameters are not context sensitive.
1152111525
if (node.typeParameters) {
1152211526
return false;
@@ -18172,7 +18176,7 @@ namespace ts {
1817218176
}
1817318177

1817418178
// Return contextual type of parameter or undefined if no contextual type is available
18175-
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type | undefined {
18179+
function getContextuallyTypedParameterType(parameter: ParameterDeclaration, forCache?: boolean): Type | undefined {
1817618180
const func = parameter.parent;
1817718181
if (!isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
1817818182
return undefined;
@@ -18193,8 +18197,21 @@ namespace ts {
1819318197
links.resolvedSignature = cached;
1819418198
return type;
1819518199
}
18196-
const contextualSignature = getContextualSignature(func);
18200+
let contextualSignature = getContextualSignature(func);
1819718201
if (contextualSignature) {
18202+
if (forCache) {
18203+
// Calling the below guarantees the types are primed and assigned in the same way
18204+
// as when the parameter is reached via `checkFunctionExpressionOrObjectLiteralMethod`.
18205+
// This should prevent any uninstantiated inference variables in the contextual signature
18206+
// from leaking, and should lock in cached parameter types via `assignContextualParameterTypes`
18207+
// which we will then immediately use the results of below.
18208+
contextuallyCheckFunctionExpressionOrObjectLiteralMethod(func);
18209+
const type = getTypeOfSymbol(getMergedSymbol(func.symbol));
18210+
if (isTypeAny(type)) {
18211+
return type;
18212+
}
18213+
contextualSignature = getSignaturesOfType(type, SignatureKind.Call)[0];
18214+
}
1819818215
const index = func.parameters.indexOf(parameter) - (getThisParameter(func) ? 1 : 0);
1819918216
return parameter.dotDotDotToken && lastOrUndefined(func.parameters) === parameter ?
1820018217
getRestTypeAtPosition(contextualSignature, index) :
@@ -22978,12 +22995,18 @@ namespace ts {
2297822995
checkGrammarForGenerator(node);
2297922996
}
2298022997

22981-
const links = getNodeLinks(node);
2298222998
const type = getTypeOfSymbol(getMergedSymbol(node.symbol));
2298322999
if (isTypeAny(type)) {
2298423000
return type;
2298523001
}
2298623002

23003+
contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode);
23004+
23005+
return type;
23006+
}
23007+
23008+
function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode?: CheckMode) {
23009+
const links = getNodeLinks(node);
2298723010
// Check if function expression is contextually typed and assign parameter types if so.
2298823011
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
2298923012
const contextualSignature = getContextualSignature(node);
@@ -22993,6 +23016,10 @@ namespace ts {
2299323016
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
2299423017
links.flags |= NodeCheckFlags.ContextChecked;
2299523018
if (contextualSignature) {
23019+
const type = getTypeOfSymbol(getMergedSymbol(node.symbol));
23020+
if (isTypeAny(type)) {
23021+
return;
23022+
}
2299623023
const signature = getSignaturesOfType(type, SignatureKind.Call)[0];
2299723024
if (isContextSensitive(node)) {
2299823025
const inferenceContext = getInferenceContext(node);
@@ -23013,8 +23040,6 @@ namespace ts {
2301323040
checkSignatureDeclaration(node);
2301423041
}
2301523042
}
23016-
23017-
return type;
2301823043
}
2301923044

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

tests/baselines/reference/fallbackToBindingPatternForTypeInference.symbols

Lines changed: 1 addition & 0 deletions
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

Lines changed: 67 additions & 0 deletions
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+
});
Lines changed: 88 additions & 0 deletions
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+
});
Lines changed: 92 additions & 0 deletions
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+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class ClassA<TEntityClass> {
2+
constructor(private entity?: TEntityClass, public settings?: SettingsInterface<TEntityClass>) {
3+
4+
}
5+
}
6+
export interface ValueInterface<TValueClass> {
7+
func?: (row: TValueClass) => any;
8+
value?: string;
9+
}
10+
export interface SettingsInterface<TClass> {
11+
values?: (row: TClass) => ValueInterface<TClass>[],
12+
}
13+
class ConcreteClass {
14+
theName = 'myClass';
15+
}
16+
17+
var thisGetsTheFalseError = new ClassA(new ConcreteClass(), {
18+
values: o => [
19+
{
20+
value: o.theName,
21+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
22+
}
23+
]
24+
});
25+
26+
var thisIsOk = new ClassA<ConcreteClass>(new ConcreteClass(), {
27+
values: o => [
28+
{
29+
value: o.theName,
30+
func: x => 'asdfkjhgfdfghjkjhgfdfghjklkjhgfdfghjklkjhgfghj'
31+
}
32+
]
33+
});

0 commit comments

Comments
 (0)