Skip to content

Commit d59e51b

Browse files
authored
Merge pull request #30215 from Microsoft/higherOrderFunctionTypeInference
Higher order function type inference
2 parents a887c6b + a9e924b commit d59e51b

13 files changed

+2555
-126
lines changed

Diff for: src/compiler/checker.ts

+235-106
Large diffs are not rendered by default.

Diff for: src/compiler/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4362,6 +4362,7 @@ namespace ts {
43624362
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
43634363
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
43644364
NoFixing = 1 << 2, // Disable type parameter fixing
4365+
SkippedGenericFunction = 1 << 3,
43654366
}
43664367

43674368
/**
@@ -4391,6 +4392,7 @@ namespace ts {
43914392
flags: InferenceFlags; // Inference flags
43924393
compareTypes: TypeComparer; // Type comparer function
43934394
returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any)
4395+
inferredTypeParameters?: ReadonlyArray<TypeParameter>;
43944396
}
43954397

43964398
/* @internal */

Diff for: src/services/services.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ namespace ts {
17981798
const span = createTextSpanFromBounds(start, end);
17991799
const formatContext = formatting.getFormatContext(formatOptions);
18001800

1801-
return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => {
1801+
return flatMap(deduplicate<number>(errorCodes, equateValues, compareValues), errorCode => {
18021802
cancellationToken.throwIfCancellationRequested();
18031803
return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences });
18041804
});

Diff for: tests/baselines/reference/contextualSignatureInstantiation1.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ var e = <K>(x: string, y?: K) => x.length;
1515
>length : number
1616

1717
var r99 = map(e); // should be {}[] for S since a generic lambda is not inferentially typed
18-
>r99 : (a: {}[]) => number[]
19-
>map(e) : (a: {}[]) => number[]
18+
>r99 : <K>(a: string[]) => number[]
19+
>map(e) : <K>(a: string[]) => number[]
2020
>map : <S, T>(f: (x: S) => T) => (a: S[]) => T[]
2121
>e : <K>(x: string, y?: K) => number
2222

@@ -37,8 +37,8 @@ var e2 = <K>(x: string, y?: K) => x.length;
3737
>length : number
3838

3939
var r100 = map2(e2); // type arg inference should fail for S since a generic lambda is not inferentially typed. Falls back to { length: number }
40-
>r100 : (a: { length: number; }[]) => number[]
41-
>map2(e2) : (a: { length: number; }[]) => number[]
40+
>r100 : <K>(a: string[]) => number[]
41+
>map2(e2) : <K>(a: string[]) => number[]
4242
>map2 : <S extends { length: number; }, T>(f: (x: S) => T) => (a: S[]) => T[]
4343
>e2 : <K>(x: string, y?: K) => number
4444

Diff for: tests/baselines/reference/contextualSignatureInstantiation2.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ var id: <T>(x:T) => T;
3131
>x : T
3232

3333
var r23 = dot(id)(id);
34-
>r23 : (_: {}) => {}
35-
>dot(id)(id) : (_: {}) => {}
34+
>r23 : <T>(_: T) => {}
35+
>dot(id)(id) : <T>(_: T) => {}
3636
>dot(id) : <U>(g: (_: U) => {}) => (_: U) => {}
3737
>dot : <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (_: U) => S
3838
>id : <T>(x: T) => T
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
tests/cases/compiler/genericFunctionInference1.ts(83,14): error TS2345: Argument of type '<a>(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'.
2+
Type 'number' is not assignable to type 'string'.
3+
4+
5+
==== tests/cases/compiler/genericFunctionInference1.ts (1 errors) ====
6+
declare function pipe<A extends any[], B>(ab: (...args: A) => B): (...args: A) => B;
7+
declare function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C;
8+
declare function pipe<A extends any[], B, C, D>(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D;
9+
10+
declare function list<T>(a: T): T[];
11+
declare function box<V>(x: V): { value: V };
12+
declare function foo<T extends { value: T }>(x: T): T;
13+
14+
const f00 = pipe(list);
15+
const f01 = pipe(list, box);
16+
const f02 = pipe(box, list);
17+
const f03 = pipe(x => list(x), box);
18+
const f04 = pipe(list, x => box(x));
19+
const f05 = pipe(x => list(x), x => box(x))
20+
const f06 = pipe(list, pipe(box));
21+
const f07 = pipe(x => list(x), pipe(box));
22+
const f08 = pipe(x => list(x), pipe(x => box(x)));
23+
const f09 = pipe(list, x => x.length);
24+
const f10 = pipe(foo);
25+
const f11 = pipe(foo, foo);
26+
27+
const g00: <T>(x: T) => T[] = pipe(list);
28+
const g01: <T>(x: T) => { value: T[] } = pipe(list, box);
29+
const g02: <T>(x: T) => { value: T }[] = pipe(box, list);
30+
const g03: <T>(x: T) => { value: T[] } = pipe(x => list(x), box);
31+
const g04: <T>(x: T) => { value: T[] } = pipe(list, x => box(x));
32+
const g05: <T>(x: T) => { value: T[] } = pipe(x => list(x), x => box(x))
33+
const g06: <T>(x: T) => { value: T[] } = pipe(list, pipe(box));
34+
const g07: <T>(x: T) => { value: T[] } = pipe(x => list(x), pipe(box));
35+
const g08: <T>(x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x)));
36+
const g09: <T>(x: T) => number = pipe(list, x => x.length);
37+
const g10: <T extends { value: T }>(x: T) => T = pipe(foo);
38+
const g12: <T extends { value: T }>(x: T) => T = pipe(foo, foo);
39+
40+
declare function pipe2<A, B, C, D>(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D];
41+
42+
const f20 = pipe2(list, box);
43+
const f21 = pipe2(box, list);
44+
const f22 = pipe2(list, list);
45+
const f23 = pipe2(box, box);
46+
const f24 = pipe2(f20, f20);
47+
const f25 = pipe2(foo, foo);
48+
const f26 = pipe2(f25, f25);
49+
50+
declare function pipe3<A, B, C>(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C];
51+
52+
const f30 = pipe3(list, box);
53+
const f31 = pipe3(box, list);
54+
const f32 = pipe3(list, list);
55+
56+
declare function pipe4<A, B, C>(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C;
57+
58+
const f40 = pipe4([list, box]);
59+
const f41 = pipe4([box, list]);
60+
61+
declare function pipe5<A, B>(f: (a: A) => B): { f: (a: A) => B };
62+
63+
const f50 = pipe5(list); // No higher order inference
64+
65+
// #417
66+
67+
function mirror<A, B>(f: (a: A) => B): (a: A) => B { return f; }
68+
var identityM = mirror(identity);
69+
70+
var x = 1;
71+
var y = identity(x);
72+
var z = identityM(x);
73+
74+
// #3038
75+
76+
export function keyOf<a>(value: { key: a; }): a {
77+
return value.key;
78+
}
79+
export interface Data {
80+
key: number;
81+
value: Date;
82+
}
83+
84+
var data: Data[] = [];
85+
86+
declare function toKeys<a>(values: a[], toKey: (value: a) => string): string[];
87+
88+
toKeys(data, keyOf); // Error
89+
~~~~~
90+
!!! error TS2345: Argument of type '<a>(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'.
91+
!!! error TS2345: Type 'number' is not assignable to type 'string'.
92+
93+
// #9366
94+
95+
function flip<a, b, c>(f: (a: a, b: b) => c): (b: b, a: a) => c {
96+
return (b: b, a: a) => f(a, b);
97+
}
98+
function zip<T, U>(x: T, y: U): [T, U] {
99+
return [x, y];
100+
}
101+
102+
var expected: <T, U>(y: U, x: T) => [T, U] = flip(zip);
103+
var actual = flip(zip);
104+
105+
// #9366
106+
107+
const map = <T, U>(transform: (t: T) => U) =>
108+
(arr: T[]) => arr.map(transform)
109+
110+
const identityStr = (t: string) => t;
111+
112+
const arr: string[] = map(identityStr)(['a']);
113+
const arr1: string[] = map(identity)(['a']);
114+
115+
// #9949
116+
117+
function of2<a, b>(one: a, two: b): [a, b] {
118+
return [one, two];
119+
}
120+
121+
const flipped = flip(of2);
122+
123+
// #29904.1
124+
125+
type Component<P> = (props: P) => {};
126+
127+
declare const myHoc1: <P>(C: Component<P>) => Component<P>;
128+
declare const myHoc2: <P>(C: Component<P>) => Component<P>;
129+
130+
declare const MyComponent1: Component<{ foo: 1 }>;
131+
132+
const enhance = pipe(
133+
myHoc1,
134+
myHoc2,
135+
);
136+
137+
const MyComponent2 = enhance(MyComponent1);
138+
139+
// #29904.2
140+
141+
const fn20 = pipe((_a?: {}) => 1);
142+
143+
// #29904.3
144+
145+
type Fn = (n: number) => number;
146+
const fn30: Fn = pipe(
147+
x => x + 1,
148+
x => x * 2,
149+
);
150+
151+
const promise = Promise.resolve(1);
152+
promise.then(
153+
pipe(
154+
x => x + 1,
155+
x => x * 2,
156+
),
157+
);
158+
159+
// #29904.4
160+
161+
declare const getString: () => string;
162+
declare const orUndefined: (name: string) => string | undefined;
163+
declare const identity: <T>(value: T) => T;
164+
165+
const fn40 = pipe(
166+
getString,
167+
string => orUndefined(string),
168+
identity,
169+
);
170+
171+
// #29904.6
172+
173+
declare const getArray: () => string[];
174+
declare const first: <T>(ts: T[]) => T;
175+
176+
const fn60 = pipe(
177+
getArray,
178+
x => x,
179+
first,
180+
);
181+
182+
const fn61 = pipe(
183+
getArray,
184+
identity,
185+
first,
186+
);
187+
188+
const fn62 = pipe(
189+
getArray,
190+
x => x,
191+
x => first(x),
192+
);
193+

0 commit comments

Comments
 (0)