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 bcf84f4

Browse filesBrowse files
authored
Merge pull request microsoft#16072 from Microsoft/improveTypeArgumentInference
Infer from generic function return types
2 parents 3186fc4 + 7ca91f8 commit bcf84f4
Copy full SHA for bcf84f4

12 files changed

+1,801-172Lines changed: 1801 additions & 172 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
+159-129Lines changed: 159 additions & 129 deletions
Large diffs are not rendered by default.
Collapse file

‎src/compiler/types.ts‎

Copy file name to clipboardExpand all lines: src/compiler/types.ts
+22-16Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,30 +3343,36 @@ namespace ts {
33433343
(t: TypeParameter): Type;
33443344
mappedTypes?: Type[]; // Types mapped by this mapper
33453345
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
3346-
context?: InferenceContext; // The inference context this mapper was created from.
3347-
// Only inference mappers have this set (in createInferenceMapper).
3348-
// The identity mapper and regular instantiation mappers do not need it.
33493346
}
33503347

3351-
/* @internal */
3352-
export interface TypeInferences {
3353-
primary: Type[]; // Inferences made directly to a type parameter
3354-
secondary: Type[]; // Inferences made to a type parameter in a union type
3355-
topLevel: boolean; // True if all inferences were made from top-level (not nested in object type) locations
3356-
isFixed: boolean; // Whether the type parameter is fixed, as defined in section 4.12.2 of the TypeScript spec
3357-
// If a type parameter is fixed, no more inferences can be made for the type parameter
3348+
export const enum InferencePriority {
3349+
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
3350+
MappedType = 1 << 1, // Reverse inference for mapped type
3351+
ReturnType = 1 << 2, // Inference made from return type of generic function
3352+
}
3353+
3354+
export interface InferenceInfo {
3355+
typeParameter: TypeParameter;
3356+
candidates: Type[];
3357+
inferredType: Type;
3358+
priority: InferencePriority;
3359+
topLevel: boolean;
3360+
isFixed: boolean;
3361+
}
3362+
3363+
export const enum InferenceFlags {
3364+
InferUnionTypes = 1 << 0, // Infer union types for disjoint candidates (otherwise unknownType)
3365+
NoDefault = 1 << 1, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
3366+
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
33583367
}
33593368

33603369
/* @internal */
3361-
export interface InferenceContext {
3370+
export interface InferenceContext extends TypeMapper {
33623371
signature: Signature; // Generic signature for which inferences are made
3363-
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
3364-
inferences: TypeInferences[]; // Inferences made for each type parameter
3365-
inferredTypes: Type[]; // Inferred type for each type parameter
3366-
mapper?: TypeMapper; // Type mapper for this inference context
3372+
inferences: InferenceInfo[]; // Inferences made for each type parameter
3373+
flags: InferenceFlags; // Inference flags
33673374
failedTypeParameterIndex?: number; // Index of type parameter for which inference failed
33683375
// It is optional because in contextual signature instantiation, nothing fails
3369-
useAnyForNoInferences?: boolean; // Use any instead of {} for no inferences
33703376
}
33713377

33723378
/* @internal */
Collapse file

‎tests/baselines/reference/implicitAnyGenerics.types‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/implicitAnyGenerics.types
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var c3 = new C<number>();
2626
var c4: C<any> = new C();
2727
>c4 : C<any>
2828
>C : C<T>
29-
>new C() : C<{}>
29+
>new C() : C<any>
3030
>C : typeof C
3131

3232
class D<T> {
Collapse file
+77Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts(68,16): error TS2339: Property 'toUpperCase' does not exist on type 'number'.
2+
3+
4+
==== tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts (1 errors) ====
5+
// Repro from #15680
6+
7+
// This is a contrived class. We could do the same thing with Observables, etc.
8+
class SetOf<A> {
9+
_store: A[];
10+
11+
add(a: A) {
12+
this._store.push(a);
13+
}
14+
15+
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
16+
return transformer(this);
17+
}
18+
19+
forEach(fn: (a: A, index: number) => void) {
20+
this._store.forEach((a, i) => fn(a, i));
21+
}
22+
}
23+
24+
function compose<A, B, C, D, E>(
25+
fnA: (a: SetOf<A>) => SetOf<B>,
26+
fnB: (b: SetOf<B>) => SetOf<C>,
27+
fnC: (c: SetOf<C>) => SetOf<D>,
28+
fnD: (c: SetOf<D>) => SetOf<E>,
29+
):(x: SetOf<A>) => SetOf<E>;
30+
/* ... etc ... */
31+
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
32+
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
33+
}
34+
35+
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
36+
return (a: SetOf<A>) => {
37+
const b: SetOf<B> = new SetOf();
38+
a.forEach(x => b.add(fn(x)));
39+
return b;
40+
}
41+
}
42+
43+
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
44+
return (a: SetOf<A>) => {
45+
const result = new SetOf<A>();
46+
a.forEach(x => {
47+
if (predicate(x)) result.add(x);
48+
});
49+
return result;
50+
}
51+
}
52+
53+
const testSet = new SetOf<number>();
54+
testSet.add(1);
55+
testSet.add(2);
56+
testSet.add(3);
57+
58+
testSet.transform(
59+
compose(
60+
filter(x => x % 1 === 0),
61+
map(x => x + x),
62+
map(x => x + '!!!'),
63+
map(x => x.toUpperCase())
64+
)
65+
)
66+
67+
testSet.transform(
68+
compose(
69+
filter(x => x % 1 === 0),
70+
map(x => x + x),
71+
map(x => 123), // Whoops a bug
72+
map(x => x.toUpperCase()) // causes an error!
73+
~~~~~~~~~~~
74+
!!! error TS2339: Property 'toUpperCase' does not exist on type 'number'.
75+
)
76+
)
77+
Collapse file
+123Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//// [inferFromGenericFunctionReturnTypes1.ts]
2+
// Repro from #15680
3+
4+
// This is a contrived class. We could do the same thing with Observables, etc.
5+
class SetOf<A> {
6+
_store: A[];
7+
8+
add(a: A) {
9+
this._store.push(a);
10+
}
11+
12+
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
13+
return transformer(this);
14+
}
15+
16+
forEach(fn: (a: A, index: number) => void) {
17+
this._store.forEach((a, i) => fn(a, i));
18+
}
19+
}
20+
21+
function compose<A, B, C, D, E>(
22+
fnA: (a: SetOf<A>) => SetOf<B>,
23+
fnB: (b: SetOf<B>) => SetOf<C>,
24+
fnC: (c: SetOf<C>) => SetOf<D>,
25+
fnD: (c: SetOf<D>) => SetOf<E>,
26+
):(x: SetOf<A>) => SetOf<E>;
27+
/* ... etc ... */
28+
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
29+
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
30+
}
31+
32+
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
33+
return (a: SetOf<A>) => {
34+
const b: SetOf<B> = new SetOf();
35+
a.forEach(x => b.add(fn(x)));
36+
return b;
37+
}
38+
}
39+
40+
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
41+
return (a: SetOf<A>) => {
42+
const result = new SetOf<A>();
43+
a.forEach(x => {
44+
if (predicate(x)) result.add(x);
45+
});
46+
return result;
47+
}
48+
}
49+
50+
const testSet = new SetOf<number>();
51+
testSet.add(1);
52+
testSet.add(2);
53+
testSet.add(3);
54+
55+
testSet.transform(
56+
compose(
57+
filter(x => x % 1 === 0),
58+
map(x => x + x),
59+
map(x => x + '!!!'),
60+
map(x => x.toUpperCase())
61+
)
62+
)
63+
64+
testSet.transform(
65+
compose(
66+
filter(x => x % 1 === 0),
67+
map(x => x + x),
68+
map(x => 123), // Whoops a bug
69+
map(x => x.toUpperCase()) // causes an error!
70+
)
71+
)
72+
73+
74+
//// [inferFromGenericFunctionReturnTypes1.js]
75+
// Repro from #15680
76+
// This is a contrived class. We could do the same thing with Observables, etc.
77+
var SetOf = (function () {
78+
function SetOf() {
79+
}
80+
SetOf.prototype.add = function (a) {
81+
this._store.push(a);
82+
};
83+
SetOf.prototype.transform = function (transformer) {
84+
return transformer(this);
85+
};
86+
SetOf.prototype.forEach = function (fn) {
87+
this._store.forEach(function (a, i) { return fn(a, i); });
88+
};
89+
return SetOf;
90+
}());
91+
/* ... etc ... */
92+
function compose() {
93+
var fns = [];
94+
for (var _i = 0; _i < arguments.length; _i++) {
95+
fns[_i] = arguments[_i];
96+
}
97+
return function (x) { return fns.reduce(function (prev, fn) { return fn(prev); }, x); };
98+
}
99+
function map(fn) {
100+
return function (a) {
101+
var b = new SetOf();
102+
a.forEach(function (x) { return b.add(fn(x)); });
103+
return b;
104+
};
105+
}
106+
function filter(predicate) {
107+
return function (a) {
108+
var result = new SetOf();
109+
a.forEach(function (x) {
110+
if (predicate(x))
111+
result.add(x);
112+
});
113+
return result;
114+
};
115+
}
116+
var testSet = new SetOf();
117+
testSet.add(1);
118+
testSet.add(2);
119+
testSet.add(3);
120+
testSet.transform(compose(filter(function (x) { return x % 1 === 0; }), map(function (x) { return x + x; }), map(function (x) { return x + '!!!'; }), map(function (x) { return x.toUpperCase(); })));
121+
testSet.transform(compose(filter(function (x) { return x % 1 === 0; }), map(function (x) { return x + x; }), map(function (x) { return 123; }), // Whoops a bug
122+
map(function (x) { return x.toUpperCase(); }) // causes an error!
123+
));

0 commit comments

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