Skip to content

Commit 7dc1f40

Browse files
authored
Merge pull request #31680 from microsoft/fixGenericReturnTypeInference
Fix generic return type inference
2 parents 1fe9a0a + 08d8f97 commit 7dc1f40

6 files changed

+227
-10
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14933,13 +14933,6 @@ namespace ts {
1493314933
return context && createInferenceContextWorker(map(context.inferences, cloneInferenceInfo), context.signature, context.flags | extraFlags, context.compareTypes);
1493414934
}
1493514935

14936-
function cloneInferredPartOfContext(context: InferenceContext): InferenceContext | undefined {
14937-
const inferences = filter(context.inferences, hasInferenceCandidates);
14938-
return inferences.length ?
14939-
createInferenceContextWorker(map(inferences, cloneInferenceInfo), context.signature, context.flags, context.compareTypes) :
14940-
undefined;
14941-
}
14942-
1494314936
function createInferenceContextWorker(inferences: InferenceInfo[], signature: Signature | undefined, flags: InferenceFlags, compareTypes: TypeComparer): InferenceContext {
1494414937
const context: InferenceContext = {
1494514938
inferences,
@@ -20912,7 +20905,8 @@ namespace ts {
2091220905
// We clone the inference context to avoid disturbing a resolution in progress for an
2091320906
// outer call expression. Effectively we just want a snapshot of whatever has been
2091420907
// inferred for any outer call expression so far.
20915-
const outerMapper = getMapperFromContext(cloneInferenceContext(getInferenceContext(node), InferenceFlags.NoDefault));
20908+
const outerContext = getInferenceContext(node);
20909+
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
2091620910
const instantiatedType = instantiateType(contextualType, outerMapper);
2091720911
// If the contextual type is a generic function type with a single call signature, we
2091820912
// instantiate the type with its own type parameters and type arguments. This ensures that
@@ -20929,8 +20923,13 @@ namespace ts {
2092920923
// Inferences made from return types have lower priority than all other inferences.
2093020924
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
2093120925
// Create a type mapper for instantiating generic contextual types using the inferences made
20932-
// from the return type.
20933-
context.returnMapper = getMapperFromContext(cloneInferredPartOfContext(context));
20926+
// from the return type. We need a separate inference pass here because (a) instantiation of
20927+
// the source type uses the outer context's return mapper (which excludes inferences made from
20928+
// outer arguments), and (b) we don't want any further inferences going into this context.
20929+
const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
20930+
const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
20931+
inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
20932+
context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(returnContext) : undefined;
2093420933
}
2093520934
}
2093620935

tests/baselines/reference/inferFromGenericFunctionReturnTypes3.errors.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,23 @@ tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts(180,26): error TS23
200200
!!! error TS2322: Types of property 'state' are incompatible.
201201
!!! error TS2322: Type 'State.B' is not assignable to type 'State.A'.
202202
!!! related TS6502 tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts:179:28: The expected type comes from the return type of this signature.
203+
204+
// Repros from #31443
205+
206+
enum Enum { A, B }
207+
208+
class ClassWithConvert<T> {
209+
constructor(val: T) { }
210+
convert(converter: { to: (v: T) => T; }) { }
211+
}
212+
213+
function fn<T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) { }
214+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
215+
216+
type Func<T> = (x: T) => T;
217+
218+
declare function makeFoo<T>(x: T): Func<T>;
219+
declare function baz<U>(x: Func<U>, y: Func<U>): void;
220+
221+
baz(makeFoo(Enum.A), makeFoo(Enum.A));
203222

tests/baselines/reference/inferFromGenericFunctionReturnTypes3.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ enum State { A, B }
179179
type Foo = { state: State }
180180
declare function bar<T>(f: () => T[]): T[];
181181
let x: Foo[] = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]); // Error
182+
183+
// Repros from #31443
184+
185+
enum Enum { A, B }
186+
187+
class ClassWithConvert<T> {
188+
constructor(val: T) { }
189+
convert(converter: { to: (v: T) => T; }) { }
190+
}
191+
192+
function fn<T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) { }
193+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
194+
195+
type Func<T> = (x: T) => T;
196+
197+
declare function makeFoo<T>(x: T): Func<T>;
198+
declare function baz<U>(x: Func<U>, y: Func<U>): void;
199+
200+
baz(makeFoo(Enum.A), makeFoo(Enum.A));
182201

183202

184203
//// [inferFromGenericFunctionReturnTypes3.js]
@@ -278,6 +297,19 @@ var State;
278297
State[State["B"] = 1] = "B";
279298
})(State || (State = {}));
280299
let x = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]); // Error
300+
// Repros from #31443
301+
var Enum;
302+
(function (Enum) {
303+
Enum[Enum["A"] = 0] = "A";
304+
Enum[Enum["B"] = 1] = "B";
305+
})(Enum || (Enum = {}));
306+
class ClassWithConvert {
307+
constructor(val) { }
308+
convert(converter) { }
309+
}
310+
function fn(arg, f) { }
311+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
312+
baz(makeFoo(Enum.A), makeFoo(Enum.A));
281313

282314

283315
//// [inferFromGenericFunctionReturnTypes3.d.ts]

tests/baselines/reference/inferFromGenericFunctionReturnTypes3.symbols

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,84 @@ let x: Foo[] = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]);
459459
>State : Symbol(State, Decl(inferFromGenericFunctionReturnTypes3.ts, 174, 56))
460460
>B : Symbol(State.B, Decl(inferFromGenericFunctionReturnTypes3.ts, 176, 15))
461461

462+
// Repros from #31443
463+
464+
enum Enum { A, B }
465+
>Enum : Symbol(Enum, Decl(inferFromGenericFunctionReturnTypes3.ts, 179, 79))
466+
>A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
467+
>B : Symbol(Enum.B, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 14))
468+
469+
class ClassWithConvert<T> {
470+
>ClassWithConvert : Symbol(ClassWithConvert, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 18))
471+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 185, 23))
472+
473+
constructor(val: T) { }
474+
>val : Symbol(val, Decl(inferFromGenericFunctionReturnTypes3.ts, 186, 14))
475+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 185, 23))
476+
477+
convert(converter: { to: (v: T) => T; }) { }
478+
>convert : Symbol(ClassWithConvert.convert, Decl(inferFromGenericFunctionReturnTypes3.ts, 186, 25))
479+
>converter : Symbol(converter, Decl(inferFromGenericFunctionReturnTypes3.ts, 187, 10))
480+
>to : Symbol(to, Decl(inferFromGenericFunctionReturnTypes3.ts, 187, 22))
481+
>v : Symbol(v, Decl(inferFromGenericFunctionReturnTypes3.ts, 187, 28))
482+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 185, 23))
483+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 185, 23))
484+
}
485+
486+
function fn<T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) { }
487+
>fn : Symbol(fn, Decl(inferFromGenericFunctionReturnTypes3.ts, 188, 1))
488+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 190, 12))
489+
>arg : Symbol(arg, Decl(inferFromGenericFunctionReturnTypes3.ts, 190, 15))
490+
>ClassWithConvert : Symbol(ClassWithConvert, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 18))
491+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 190, 12))
492+
>f : Symbol(f, Decl(inferFromGenericFunctionReturnTypes3.ts, 190, 40))
493+
>ClassWithConvert : Symbol(ClassWithConvert, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 18))
494+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 190, 12))
495+
496+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
497+
>fn : Symbol(fn, Decl(inferFromGenericFunctionReturnTypes3.ts, 188, 1))
498+
>ClassWithConvert : Symbol(ClassWithConvert, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 18))
499+
>Enum.A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
500+
>Enum : Symbol(Enum, Decl(inferFromGenericFunctionReturnTypes3.ts, 179, 79))
501+
>A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
502+
>ClassWithConvert : Symbol(ClassWithConvert, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 18))
503+
>Enum.A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
504+
>Enum : Symbol(Enum, Decl(inferFromGenericFunctionReturnTypes3.ts, 179, 79))
505+
>A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
506+
507+
type Func<T> = (x: T) => T;
508+
>Func : Symbol(Func, Decl(inferFromGenericFunctionReturnTypes3.ts, 191, 69))
509+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 10))
510+
>x : Symbol(x, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 16))
511+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 10))
512+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 10))
513+
514+
declare function makeFoo<T>(x: T): Func<T>;
515+
>makeFoo : Symbol(makeFoo, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 27))
516+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 25))
517+
>x : Symbol(x, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 28))
518+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 25))
519+
>Func : Symbol(Func, Decl(inferFromGenericFunctionReturnTypes3.ts, 191, 69))
520+
>T : Symbol(T, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 25))
521+
522+
declare function baz<U>(x: Func<U>, y: Func<U>): void;
523+
>baz : Symbol(baz, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 43))
524+
>U : Symbol(U, Decl(inferFromGenericFunctionReturnTypes3.ts, 196, 21))
525+
>x : Symbol(x, Decl(inferFromGenericFunctionReturnTypes3.ts, 196, 24))
526+
>Func : Symbol(Func, Decl(inferFromGenericFunctionReturnTypes3.ts, 191, 69))
527+
>U : Symbol(U, Decl(inferFromGenericFunctionReturnTypes3.ts, 196, 21))
528+
>y : Symbol(y, Decl(inferFromGenericFunctionReturnTypes3.ts, 196, 35))
529+
>Func : Symbol(Func, Decl(inferFromGenericFunctionReturnTypes3.ts, 191, 69))
530+
>U : Symbol(U, Decl(inferFromGenericFunctionReturnTypes3.ts, 196, 21))
531+
532+
baz(makeFoo(Enum.A), makeFoo(Enum.A));
533+
>baz : Symbol(baz, Decl(inferFromGenericFunctionReturnTypes3.ts, 195, 43))
534+
>makeFoo : Symbol(makeFoo, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 27))
535+
>Enum.A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
536+
>Enum : Symbol(Enum, Decl(inferFromGenericFunctionReturnTypes3.ts, 179, 79))
537+
>A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
538+
>makeFoo : Symbol(makeFoo, Decl(inferFromGenericFunctionReturnTypes3.ts, 193, 27))
539+
>Enum.A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
540+
>Enum : Symbol(Enum, Decl(inferFromGenericFunctionReturnTypes3.ts, 179, 79))
541+
>A : Symbol(Enum.A, Decl(inferFromGenericFunctionReturnTypes3.ts, 183, 11))
542+

tests/baselines/reference/inferFromGenericFunctionReturnTypes3.types

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,70 @@ let x: Foo[] = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]);
506506
>State : typeof State
507507
>B : State.B
508508

509+
// Repros from #31443
510+
511+
enum Enum { A, B }
512+
>Enum : Enum
513+
>A : Enum.A
514+
>B : Enum.B
515+
516+
class ClassWithConvert<T> {
517+
>ClassWithConvert : ClassWithConvert<T>
518+
519+
constructor(val: T) { }
520+
>val : T
521+
522+
convert(converter: { to: (v: T) => T; }) { }
523+
>convert : (converter: { to: (v: T) => T; }) => void
524+
>converter : { to: (v: T) => T; }
525+
>to : (v: T) => T
526+
>v : T
527+
}
528+
529+
function fn<T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) { }
530+
>fn : <T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) => void
531+
>arg : ClassWithConvert<T>
532+
>f : () => ClassWithConvert<T>
533+
534+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
535+
>fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A)) : void
536+
>fn : <T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) => void
537+
>new ClassWithConvert(Enum.A) : ClassWithConvert<Enum>
538+
>ClassWithConvert : typeof ClassWithConvert
539+
>Enum.A : Enum.A
540+
>Enum : typeof Enum
541+
>A : Enum.A
542+
>() => new ClassWithConvert(Enum.A) : () => ClassWithConvert<Enum>
543+
>new ClassWithConvert(Enum.A) : ClassWithConvert<Enum>
544+
>ClassWithConvert : typeof ClassWithConvert
545+
>Enum.A : Enum.A
546+
>Enum : typeof Enum
547+
>A : Enum.A
548+
549+
type Func<T> = (x: T) => T;
550+
>Func : Func<T>
551+
>x : T
552+
553+
declare function makeFoo<T>(x: T): Func<T>;
554+
>makeFoo : <T>(x: T) => Func<T>
555+
>x : T
556+
557+
declare function baz<U>(x: Func<U>, y: Func<U>): void;
558+
>baz : <U>(x: Func<U>, y: Func<U>) => void
559+
>x : Func<U>
560+
>y : Func<U>
561+
562+
baz(makeFoo(Enum.A), makeFoo(Enum.A));
563+
>baz(makeFoo(Enum.A), makeFoo(Enum.A)) : void
564+
>baz : <U>(x: Func<U>, y: Func<U>) => void
565+
>makeFoo(Enum.A) : Func<Enum>
566+
>makeFoo : <T>(x: T) => Func<T>
567+
>Enum.A : Enum.A
568+
>Enum : typeof Enum
569+
>A : Enum.A
570+
>makeFoo(Enum.A) : Func<Enum>
571+
>makeFoo : <T>(x: T) => Func<T>
572+
>Enum.A : Enum.A
573+
>Enum : typeof Enum
574+
>A : Enum.A
575+

tests/cases/compiler/inferFromGenericFunctionReturnTypes3.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,22 @@ enum State { A, B }
182182
type Foo = { state: State }
183183
declare function bar<T>(f: () => T[]): T[];
184184
let x: Foo[] = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]); // Error
185+
186+
// Repros from #31443
187+
188+
enum Enum { A, B }
189+
190+
class ClassWithConvert<T> {
191+
constructor(val: T) { }
192+
convert(converter: { to: (v: T) => T; }) { }
193+
}
194+
195+
function fn<T>(arg: ClassWithConvert<T>, f: () => ClassWithConvert<T>) { }
196+
fn(new ClassWithConvert(Enum.A), () => new ClassWithConvert(Enum.A));
197+
198+
type Func<T> = (x: T) => T;
199+
200+
declare function makeFoo<T>(x: T): Func<T>;
201+
declare function baz<U>(x: Func<U>, y: Func<U>): void;
202+
203+
baz(makeFoo(Enum.A), makeFoo(Enum.A));

0 commit comments

Comments
 (0)