Skip to content

Commit 9cedb6a

Browse files
authored
Merge pull request #11673 from Microsoft/partiallyAnnotatedSignatures
Contextual typing of partially annotated signatures
2 parents 2321e28 + af52b63 commit 9cedb6a

14 files changed

+569
-20
lines changed

Diff for: src/compiler/checker.ts

+47-19
Original file line numberDiff line numberDiff line change
@@ -3144,8 +3144,7 @@ namespace ts {
31443144
// Use contextual parameter type if one is available
31453145
let type: Type;
31463146
if (declaration.symbol.name === "this") {
3147-
const thisParameter = getContextualThisParameter(func);
3148-
type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined;
3147+
type = getContextualThisParameterType(func);
31493148
}
31503149
else {
31513150
type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
@@ -4789,9 +4788,6 @@ namespace ts {
47894788
if (isJSConstructSignature) {
47904789
minArgumentCount--;
47914790
}
4792-
if (!thisParameter && isObjectLiteralMethod(declaration)) {
4793-
thisParameter = getContextualThisParameter(declaration);
4794-
}
47954791

47964792
const classType = declaration.kind === SyntaxKind.Constructor ?
47974793
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
@@ -6101,9 +6097,24 @@ namespace ts {
61016097
}
61026098

61036099
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
6104-
const areAllParametersUntyped = !forEach(node.parameters, p => p.type);
6105-
const isNullaryArrow = node.kind === SyntaxKind.ArrowFunction && !node.parameters.length;
6106-
return !node.typeParameters && areAllParametersUntyped && !isNullaryArrow;
6100+
// Functions with type parameters are not context sensitive.
6101+
if (node.typeParameters) {
6102+
return false;
6103+
}
6104+
// Functions with any parameters that lack type annotations are context sensitive.
6105+
if (forEach(node.parameters, p => !p.type)) {
6106+
return true;
6107+
}
6108+
// For arrow functions we now know we're not context sensitive.
6109+
if (node.kind === SyntaxKind.ArrowFunction) {
6110+
return false;
6111+
}
6112+
// If the first parameter is not an explicit 'this' parameter, then the function has
6113+
// an implicit 'this' parameter which is subject to contextual typing. Otherwise we
6114+
// know that all parameters (including 'this') have type annotations and nothing is
6115+
// subject to contextual typing.
6116+
const parameter = firstOrUndefined(node.parameters);
6117+
return !(parameter && parameterIsThisKeyword(parameter));
61076118
}
61086119

61096120
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration {
@@ -9629,7 +9640,7 @@ namespace ts {
96299640
}
96309641
}
96319642

9632-
const thisType = getThisTypeOfDeclaration(container);
9643+
const thisType = getThisTypeOfDeclaration(container) || getContextualThisParameterType(container);
96339644
if (thisType) {
96349645
return thisType;
96359646
}
@@ -9869,14 +9880,16 @@ namespace ts {
98699880
}
98709881
}
98719882

9872-
function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol {
9883+
function getContextualThisParameterType(func: FunctionLikeDeclaration): Type {
98739884
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
98749885
const contextualSignature = getContextualSignature(func);
98759886
if (contextualSignature) {
9876-
return contextualSignature.thisParameter;
9887+
const thisParameter = contextualSignature.thisParameter;
9888+
if (thisParameter) {
9889+
return getTypeOfSymbol(thisParameter);
9890+
}
98779891
}
98789892
}
9879-
98809893
return undefined;
98819894
}
98829895

@@ -12843,21 +12856,36 @@ namespace ts {
1284312856

1284412857
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) {
1284512858
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
12859+
if (isInferentialContext(mapper)) {
12860+
for (let i = 0; i < len; i++) {
12861+
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
12862+
if (declaration.type) {
12863+
inferTypes(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
12864+
}
12865+
}
12866+
}
1284612867
if (context.thisParameter) {
12847-
if (!signature.thisParameter) {
12848-
signature.thisParameter = createTransientSymbol(context.thisParameter, undefined);
12868+
const parameter = signature.thisParameter;
12869+
if (!parameter || parameter.valueDeclaration && !(<ParameterDeclaration>parameter.valueDeclaration).type) {
12870+
if (!parameter) {
12871+
signature.thisParameter = createTransientSymbol(context.thisParameter, undefined);
12872+
}
12873+
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
1284912874
}
12850-
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
1285112875
}
1285212876
for (let i = 0; i < len; i++) {
1285312877
const parameter = signature.parameters[i];
12854-
const contextualParameterType = getTypeAtPosition(context, i);
12855-
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
12878+
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
12879+
const contextualParameterType = getTypeAtPosition(context, i);
12880+
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
12881+
}
1285612882
}
1285712883
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
1285812884
const parameter = lastOrUndefined(signature.parameters);
12859-
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
12860-
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
12885+
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
12886+
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
12887+
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
12888+
}
1286112889
}
1286212890
}
1286312891

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(12,11): error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'.
2+
tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(13,11): error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'.
3+
tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(14,11): error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'.
4+
5+
6+
==== tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts (3 errors) ====
7+
class C {
8+
test: string
9+
}
10+
11+
class D extends C {
12+
test2: string
13+
}
14+
15+
declare function testError<T extends C>(a: (t: T, t1: T) => void): T
16+
17+
// more args
18+
testError((t1: D, t2, t3) => {})
19+
~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'.
21+
testError((t1, t2: D, t3) => {})
22+
~~~~~~~~~~~~~~~~~~~~~
23+
!!! error TS2345: Argument of type '(t1: D, t2: D, t3: any) => void' is not assignable to parameter of type '(t: D, t1: D) => void'.
24+
testError((t1, t2, t3: D) => {})
25+
~~~~~~~~~~~~~~~~~~~~~
26+
!!! error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'.
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [partiallyAnnotatedFunctionInferenceError.ts]
2+
class C {
3+
test: string
4+
}
5+
6+
class D extends C {
7+
test2: string
8+
}
9+
10+
declare function testError<T extends C>(a: (t: T, t1: T) => void): T
11+
12+
// more args
13+
testError((t1: D, t2, t3) => {})
14+
testError((t1, t2: D, t3) => {})
15+
testError((t1, t2, t3: D) => {})
16+
17+
18+
//// [partiallyAnnotatedFunctionInferenceError.js]
19+
var __extends = (this && this.__extends) || function (d, b) {
20+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
21+
function __() { this.constructor = d; }
22+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23+
};
24+
var C = (function () {
25+
function C() {
26+
}
27+
return C;
28+
}());
29+
var D = (function (_super) {
30+
__extends(D, _super);
31+
function D() {
32+
return _super.apply(this, arguments) || this;
33+
}
34+
return D;
35+
}(C));
36+
// more args
37+
testError(function (t1, t2, t3) { });
38+
testError(function (t1, t2, t3) { });
39+
testError(function (t1, t2, t3) { });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//// [partiallyAnnotatedFunctionInferenceWithTypeParameter.ts]
2+
class C {
3+
test: string
4+
}
5+
6+
class D extends C {
7+
test2: string
8+
}
9+
10+
declare function test<T extends C>(a: (t: T, t1: T) => void): T
11+
12+
declare function testRest<T extends C>(a: (t: T, t1: T, ...ts: T[]) => void): T
13+
14+
15+
// exactly
16+
test((t1: D, t2) => { t2.test2 })
17+
test((t1, t2: D) => { t2.test2 })
18+
19+
// zero arg
20+
test(() => {})
21+
22+
// fewer args
23+
test((t1: D) => {})
24+
25+
// rest arg
26+
test((...ts: D[]) => {})
27+
28+
// source function has rest arg
29+
testRest((t1: D) => {})
30+
testRest((t1, t2, t3) => {})
31+
testRest((t1: D, t2, t3) => {})
32+
testRest((t1, t2: D, t3) => {})
33+
testRest((t2: D, ...t3) => {})
34+
testRest((t2, ...t3: D[]) => {})
35+
36+
37+
//// [partiallyAnnotatedFunctionInferenceWithTypeParameter.js]
38+
var __extends = (this && this.__extends) || function (d, b) {
39+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
40+
function __() { this.constructor = d; }
41+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
42+
};
43+
var C = (function () {
44+
function C() {
45+
}
46+
return C;
47+
}());
48+
var D = (function (_super) {
49+
__extends(D, _super);
50+
function D() {
51+
return _super.apply(this, arguments) || this;
52+
}
53+
return D;
54+
}(C));
55+
// exactly
56+
test(function (t1, t2) { t2.test2; });
57+
test(function (t1, t2) { t2.test2; });
58+
// zero arg
59+
test(function () { });
60+
// fewer args
61+
test(function (t1) { });
62+
// rest arg
63+
test(function () {
64+
var ts = [];
65+
for (var _i = 0; _i < arguments.length; _i++) {
66+
ts[_i - 0] = arguments[_i];
67+
}
68+
});
69+
// source function has rest arg
70+
testRest(function (t1) { });
71+
testRest(function (t1, t2, t3) { });
72+
testRest(function (t1, t2, t3) { });
73+
testRest(function (t1, t2, t3) { });
74+
testRest(function (t2) {
75+
var t3 = [];
76+
for (var _i = 1; _i < arguments.length; _i++) {
77+
t3[_i - 1] = arguments[_i];
78+
}
79+
});
80+
testRest(function (t2) {
81+
var t3 = [];
82+
for (var _i = 1; _i < arguments.length; _i++) {
83+
t3[_i - 1] = arguments[_i];
84+
}
85+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
=== tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceWithTypeParameter.ts ===
2+
class C {
3+
>C : Symbol(C, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 0, 0))
4+
5+
test: string
6+
>test : Symbol(C.test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 0, 9))
7+
}
8+
9+
class D extends C {
10+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
11+
>C : Symbol(C, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 0, 0))
12+
13+
test2: string
14+
>test2 : Symbol(D.test2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 4, 19))
15+
}
16+
17+
declare function test<T extends C>(a: (t: T, t1: T) => void): T
18+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
19+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 22))
20+
>C : Symbol(C, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 0, 0))
21+
>a : Symbol(a, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 35))
22+
>t : Symbol(t, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 39))
23+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 22))
24+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 44))
25+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 22))
26+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 22))
27+
28+
declare function testRest<T extends C>(a: (t: T, t1: T, ...ts: T[]) => void): T
29+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
30+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 26))
31+
>C : Symbol(C, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 0, 0))
32+
>a : Symbol(a, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 39))
33+
>t : Symbol(t, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 43))
34+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 26))
35+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 48))
36+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 26))
37+
>ts : Symbol(ts, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 55))
38+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 26))
39+
>T : Symbol(T, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 10, 26))
40+
41+
42+
// exactly
43+
test((t1: D, t2) => { t2.test2 })
44+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
45+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 14, 6))
46+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
47+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 14, 12))
48+
>t2.test2 : Symbol(D.test2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 4, 19))
49+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 14, 12))
50+
>test2 : Symbol(D.test2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 4, 19))
51+
52+
test((t1, t2: D) => { t2.test2 })
53+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
54+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 15, 6))
55+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 15, 9))
56+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
57+
>t2.test2 : Symbol(D.test2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 4, 19))
58+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 15, 9))
59+
>test2 : Symbol(D.test2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 4, 19))
60+
61+
// zero arg
62+
test(() => {})
63+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
64+
65+
// fewer args
66+
test((t1: D) => {})
67+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
68+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 21, 6))
69+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
70+
71+
// rest arg
72+
test((...ts: D[]) => {})
73+
>test : Symbol(test, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 6, 1))
74+
>ts : Symbol(ts, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 24, 6))
75+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
76+
77+
// source function has rest arg
78+
testRest((t1: D) => {})
79+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
80+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 27, 10))
81+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
82+
83+
testRest((t1, t2, t3) => {})
84+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
85+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 28, 10))
86+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 28, 13))
87+
>t3 : Symbol(t3, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 28, 17))
88+
89+
testRest((t1: D, t2, t3) => {})
90+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
91+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 29, 10))
92+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
93+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 29, 16))
94+
>t3 : Symbol(t3, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 29, 20))
95+
96+
testRest((t1, t2: D, t3) => {})
97+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
98+
>t1 : Symbol(t1, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 30, 10))
99+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 30, 13))
100+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
101+
>t3 : Symbol(t3, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 30, 20))
102+
103+
testRest((t2: D, ...t3) => {})
104+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
105+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 31, 10))
106+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
107+
>t3 : Symbol(t3, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 31, 16))
108+
109+
testRest((t2, ...t3: D[]) => {})
110+
>testRest : Symbol(testRest, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 8, 63))
111+
>t2 : Symbol(t2, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 32, 10))
112+
>t3 : Symbol(t3, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 32, 13))
113+
>D : Symbol(D, Decl(partiallyAnnotatedFunctionInferenceWithTypeParameter.ts, 2, 1))
114+

0 commit comments

Comments
 (0)