Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5888804

Browse filesBrowse files
authored
Merge pull request microsoft#16305 from Microsoft/contextualGenericTypes
Contextual generic function types
2 parents 7608196 + 1c967c3 commit 5888804
Copy full SHA for 5888804

13 files changed

+953-50Lines changed: 953 additions & 50 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+22-12Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10261,7 +10261,7 @@ namespace ts {
1026110261
const objectFlags = getObjectFlags(type);
1026210262
return !!(type.flags & TypeFlags.TypeVariable ||
1026310263
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
10264-
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
10264+
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
1026510265
objectFlags & ObjectFlags.Mapped ||
1026610266
type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
1026710267
}
@@ -13066,13 +13066,13 @@ namespace ts {
1306613066
return node ? node.contextualMapper : identityMapper;
1306713067
}
1306813068

13069-
// If the given type is an object or union type, if that type has a single signature, and if
13070-
// that signature is non-generic, return the signature. Otherwise return undefined.
13071-
function getNonGenericSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature {
13069+
// If the given type is an object or union type with a single signature, and if that signature has at
13070+
// least as many parameters as the given function, return the signature. Otherwise return undefined.
13071+
function getContextualCallSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature {
1307213072
const signatures = getSignaturesOfStructuredType(type, SignatureKind.Call);
1307313073
if (signatures.length === 1) {
1307413074
const signature = signatures[0];
13075-
if (!signature.typeParameters && !isAritySmaller(signature, node)) {
13075+
if (!isAritySmaller(signature, node)) {
1307613076
return signature;
1307713077
}
1307813078
}
@@ -13123,12 +13123,12 @@ namespace ts {
1312313123
return undefined;
1312413124
}
1312513125
if (!(type.flags & TypeFlags.Union)) {
13126-
return getNonGenericSignature(type, node);
13126+
return getContextualCallSignature(type, node);
1312713127
}
1312813128
let signatureList: Signature[];
1312913129
const types = (<UnionType>type).types;
1313013130
for (const current of types) {
13131-
const signature = getNonGenericSignature(current, node);
13131+
const signature = getContextualCallSignature(current, node);
1313213132
if (signature) {
1313313133
if (!signatureList) {
1313413134
// This signature will contribute to contextual union signature
@@ -14988,11 +14988,21 @@ namespace ts {
1498814988
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
1498914989
// outer call expression. Effectively we just want a snapshot of whatever has been
1499014990
// inferred for any outer call expression so far.
14991-
const mapper = cloneTypeMapper(getContextualMapper(node));
14992-
const instantiatedType = instantiateType(contextualType, mapper);
14993-
const returnType = getReturnTypeOfSignature(signature);
14994-
// Inferences made from return types have lower priority than all other inferences.
14995-
inferTypes(context.inferences, instantiatedType, returnType, InferencePriority.ReturnType);
14991+
const instantiatedType = instantiateType(contextualType, cloneTypeMapper(getContextualMapper(node)));
14992+
// If the contextual type is a generic pure function type, we instantiate the type with
14993+
// its own type parameters and type arguments. This ensures that the type parameters are
14994+
// not erased to type any during type inference such that they can be inferred as actual
14995+
// types from the contextual type. For example:
14996+
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
14997+
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
14998+
// Above, the type of the 'value' parameter is inferred to be 'A'.
14999+
const contextualSignature = getSingleCallSignature(instantiatedType);
15000+
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
15001+
getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters)) :
15002+
instantiatedType;
15003+
const inferenceTargetType = getReturnTypeOfSignature(signature);
15004+
// Inferences made from return types have lower priority than all other inferences.
15005+
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
1499615006
}
1499715007
}
1499815008

Collapse file

‎tests/baselines/reference/contextualTypingWithGenericSignature.types‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/contextualTypingWithGenericSignature.types
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ var f2: {
1616
};
1717

1818
f2 = (x, y) => { return x }
19-
>f2 = (x, y) => { return x } : (x: any, y: any) => any
19+
>f2 = (x, y) => { return x } : (x: T, y: U) => T
2020
>f2 : <T, U>(x: T, y: U) => T
21-
>(x, y) => { return x } : (x: any, y: any) => any
22-
>x : any
23-
>y : any
24-
>x : any
21+
>(x, y) => { return x } : (x: T, y: U) => T
22+
>x : T
23+
>y : U
24+
>x : T
2525

Collapse file
+107Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//// [genericContextualTypes1.ts]
2+
type Box<T> = { value: T };
3+
4+
declare function wrap<A, B>(f: (a: A) => B): (a: A) => B;
5+
6+
declare function compose<A, B, C>(f: (a: A) => B, g: (b: B) => C): (a: A) => C;
7+
8+
declare function list<T>(a: T): T[];
9+
10+
declare function unlist<T>(a: T[]): T;
11+
12+
declare function box<V>(x: V): Box<V>;
13+
14+
declare function unbox<W>(x: Box<W>): W;
15+
16+
declare function map<T, U>(a: T[], f: (x: T) => U): U[];
17+
18+
declare function identity<T>(x: T): T;
19+
20+
declare function zip<A, B>(a: A, b: B): [A, B];
21+
22+
declare function flip<X, Y, Z>(f: (x: X, y: Y) => Z): (y: Y, x: X) => Z;
23+
24+
const f00: <A>(x: A) => A[] = list;
25+
const f01: <A>(x: A) => A[] = x => [x];
26+
const f02: <A>(x: A) => A[] = wrap(list);
27+
const f03: <A>(x: A) => A[] = wrap(x => [x]);
28+
29+
const f10: <T>(x: T) => Box<T[]> = compose(a => list(a), b => box(b));
30+
const f11: <T>(x: T) => Box<T[]> = compose(list, box);
31+
const f12: <T>(x: Box<T[]>) => T = compose(a => unbox(a), b => unlist(b));
32+
const f13: <T>(x: Box<T[]>) => T = compose(unbox, unlist);
33+
34+
const arrayMap = <T, U>(f: (x: T) => U) => (a: T[]) => a.map(f);
35+
const arrayFilter = <T>(f: (x: T) => boolean) => (a: T[]) => a.filter(f);
36+
37+
const f20: (a: string[]) => number[] = arrayMap(x => x.length);
38+
const f21: <A>(a: A[]) => A[][] = arrayMap(x => [x]);
39+
const f22: <A>(a: A[]) => A[] = arrayMap(identity);
40+
const f23: <A>(a: A[]) => Box<A>[] = arrayMap(value => ({ value }));
41+
42+
const f30: (a: string[]) => string[] = arrayFilter(x => x.length > 10);
43+
const f31: <T extends Box<number>>(a: T[]) => T[] = arrayFilter(x => x.value > 10);
44+
45+
const f40: <A, B>(b: B, a: A) => [A, B] = flip(zip);
46+
47+
// Repro from #16293
48+
49+
type fn = <A>(a: A) => A;
50+
const fn: fn = a => a;
51+
52+
53+
//// [genericContextualTypes1.js]
54+
"use strict";
55+
var f00 = list;
56+
var f01 = function (x) { return [x]; };
57+
var f02 = wrap(list);
58+
var f03 = wrap(function (x) { return [x]; });
59+
var f10 = compose(function (a) { return list(a); }, function (b) { return box(b); });
60+
var f11 = compose(list, box);
61+
var f12 = compose(function (a) { return unbox(a); }, function (b) { return unlist(b); });
62+
var f13 = compose(unbox, unlist);
63+
var arrayMap = function (f) { return function (a) { return a.map(f); }; };
64+
var arrayFilter = function (f) { return function (a) { return a.filter(f); }; };
65+
var f20 = arrayMap(function (x) { return x.length; });
66+
var f21 = arrayMap(function (x) { return [x]; });
67+
var f22 = arrayMap(identity);
68+
var f23 = arrayMap(function (value) { return ({ value: value }); });
69+
var f30 = arrayFilter(function (x) { return x.length > 10; });
70+
var f31 = arrayFilter(function (x) { return x.value > 10; });
71+
var f40 = flip(zip);
72+
var fn = function (a) { return a; };
73+
74+
75+
//// [genericContextualTypes1.d.ts]
76+
declare type Box<T> = {
77+
value: T;
78+
};
79+
declare function wrap<A, B>(f: (a: A) => B): (a: A) => B;
80+
declare function compose<A, B, C>(f: (a: A) => B, g: (b: B) => C): (a: A) => C;
81+
declare function list<T>(a: T): T[];
82+
declare function unlist<T>(a: T[]): T;
83+
declare function box<V>(x: V): Box<V>;
84+
declare function unbox<W>(x: Box<W>): W;
85+
declare function map<T, U>(a: T[], f: (x: T) => U): U[];
86+
declare function identity<T>(x: T): T;
87+
declare function zip<A, B>(a: A, b: B): [A, B];
88+
declare function flip<X, Y, Z>(f: (x: X, y: Y) => Z): (y: Y, x: X) => Z;
89+
declare const f00: <A>(x: A) => A[];
90+
declare const f01: <A>(x: A) => A[];
91+
declare const f02: <A>(x: A) => A[];
92+
declare const f03: <A>(x: A) => A[];
93+
declare const f10: <T>(x: T) => Box<T[]>;
94+
declare const f11: <T>(x: T) => Box<T[]>;
95+
declare const f12: <T>(x: Box<T[]>) => T;
96+
declare const f13: <T>(x: Box<T[]>) => T;
97+
declare const arrayMap: <T, U>(f: (x: T) => U) => (a: T[]) => U[];
98+
declare const arrayFilter: <T>(f: (x: T) => boolean) => (a: T[]) => T[];
99+
declare const f20: (a: string[]) => number[];
100+
declare const f21: <A>(a: A[]) => A[][];
101+
declare const f22: <A>(a: A[]) => A[];
102+
declare const f23: <A>(a: A[]) => Box<A>[];
103+
declare const f30: (a: string[]) => string[];
104+
declare const f31: <T extends Box<number>>(a: T[]) => T[];
105+
declare const f40: <A, B>(b: B, a: A) => [A, B];
106+
declare type fn = <A>(a: A) => A;
107+
declare const fn: fn;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.