From a6a3ae00a614ec76272ed101557a5ea121cd81f5 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 28 Feb 2019 12:46:24 -0800 Subject: [PATCH] Only collect inferences which actually have inferences into the returnMapper (#30111) --- src/compiler/checker.ts | 21 +++++- .../returnTypeInferenceNotTooBroad.js | 24 +++++++ .../returnTypeInferenceNotTooBroad.symbols | 67 +++++++++++++++++++ .../returnTypeInferenceNotTooBroad.types | 65 ++++++++++++++++++ .../returnTypeInferenceNotTooBroad.ts | 14 ++++ 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/returnTypeInferenceNotTooBroad.js create mode 100644 tests/baselines/reference/returnTypeInferenceNotTooBroad.symbols create mode 100644 tests/baselines/reference/returnTypeInferenceNotTooBroad.types create mode 100644 tests/cases/compiler/returnTypeInferenceNotTooBroad.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf61c4a97a8..363eeca7894 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10691,6 +10691,23 @@ namespace ts { mapper; } + function cloneInferredPartOfContext(context: InferenceContext): InferenceContext | undefined { + // Filter context to only those parameters which actually have inference candidates + const params = []; + const inferences = []; + for (let i = 0; i < context.typeParameters.length; i++) { + const info = context.inferences[i]; + if (info.candidates || info.contraCandidates) { + params.push(context.typeParameters[i]); + inferences.push(info); + } + } + if (!params.length) { + return undefined; + } + return createInferenceContext(params, context.signature, context.flags | InferenceFlags.NoDefault, context.compareTypes, inferences); + } + function combineTypeMappers(mapper1: TypeMapper | undefined, mapper2: TypeMapper): TypeMapper; function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper | undefined): TypeMapper; function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper { @@ -14900,7 +14917,7 @@ namespace ts { // parameter should be instantiated to the empty object type. inferredType = instantiateType(defaultType, combineTypeMappers( - createBackreferenceMapper(context.signature!.typeParameters!, index), + createBackreferenceMapper(context.typeParameters, index), context)); } else { @@ -20069,7 +20086,7 @@ namespace ts { inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); // Create a type mapper for instantiating generic contextual types using the inferences made // from the return type. - context.returnMapper = cloneTypeMapper(context); + context.returnMapper = cloneInferredPartOfContext(context); } } diff --git a/tests/baselines/reference/returnTypeInferenceNotTooBroad.js b/tests/baselines/reference/returnTypeInferenceNotTooBroad.js new file mode 100644 index 00000000000..74ee34f508b --- /dev/null +++ b/tests/baselines/reference/returnTypeInferenceNotTooBroad.js @@ -0,0 +1,24 @@ +//// [returnTypeInferenceNotTooBroad.ts] +type Signs = { kind: 'a'; a: 3; } | { kind: 'b'; b: 2; } | { kind: 'c'; c: 1; }; +interface Opts { + low?: number; + sign?: T +} +interface Wrapper { +} +declare function sepsis(opts: Opts): Wrapper; +declare function unwrap(w: Wrapper): T; +export const y = sepsis({ low: 1, sign: { kind: 'a', a: 3 }}); +// $ExpectType { kind: "a"; a: 3; } +export const yun = unwrap(y); +// $ExpectType { kind: "a"; a: 3; } +export const yone = unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 }})); + +//// [returnTypeInferenceNotTooBroad.js] +"use strict"; +exports.__esModule = true; +exports.y = sepsis({ low: 1, sign: { kind: 'a', a: 3 } }); +// $ExpectType { kind: "a"; a: 3; } +exports.yun = unwrap(exports.y); +// $ExpectType { kind: "a"; a: 3; } +exports.yone = unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 } })); diff --git a/tests/baselines/reference/returnTypeInferenceNotTooBroad.symbols b/tests/baselines/reference/returnTypeInferenceNotTooBroad.symbols new file mode 100644 index 00000000000..6a79c2f29e3 --- /dev/null +++ b/tests/baselines/reference/returnTypeInferenceNotTooBroad.symbols @@ -0,0 +1,67 @@ +=== tests/cases/compiler/returnTypeInferenceNotTooBroad.ts === +type Signs = { kind: 'a'; a: 3; } | { kind: 'b'; b: 2; } | { kind: 'c'; c: 1; }; +>Signs : Symbol(Signs, Decl(returnTypeInferenceNotTooBroad.ts, 0, 0)) +>kind : Symbol(kind, Decl(returnTypeInferenceNotTooBroad.ts, 0, 14)) +>a : Symbol(a, Decl(returnTypeInferenceNotTooBroad.ts, 0, 25)) +>kind : Symbol(kind, Decl(returnTypeInferenceNotTooBroad.ts, 0, 37)) +>b : Symbol(b, Decl(returnTypeInferenceNotTooBroad.ts, 0, 48)) +>kind : Symbol(kind, Decl(returnTypeInferenceNotTooBroad.ts, 0, 60)) +>c : Symbol(c, Decl(returnTypeInferenceNotTooBroad.ts, 0, 71)) + +interface Opts { +>Opts : Symbol(Opts, Decl(returnTypeInferenceNotTooBroad.ts, 0, 80)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 1, 15)) + + low?: number; +>low : Symbol(Opts.low, Decl(returnTypeInferenceNotTooBroad.ts, 1, 19)) + + sign?: T +>sign : Symbol(Opts.sign, Decl(returnTypeInferenceNotTooBroad.ts, 2, 17)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 1, 15)) +} +interface Wrapper { +>Wrapper : Symbol(Wrapper, Decl(returnTypeInferenceNotTooBroad.ts, 4, 1)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 5, 18)) +} +declare function sepsis(opts: Opts): Wrapper; +>sepsis : Symbol(sepsis, Decl(returnTypeInferenceNotTooBroad.ts, 6, 1)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 7, 24)) +>Signs : Symbol(Signs, Decl(returnTypeInferenceNotTooBroad.ts, 0, 0)) +>opts : Symbol(opts, Decl(returnTypeInferenceNotTooBroad.ts, 7, 41)) +>Opts : Symbol(Opts, Decl(returnTypeInferenceNotTooBroad.ts, 0, 80)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 7, 24)) +>Wrapper : Symbol(Wrapper, Decl(returnTypeInferenceNotTooBroad.ts, 4, 1)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 7, 24)) + +declare function unwrap(w: Wrapper): T; +>unwrap : Symbol(unwrap, Decl(returnTypeInferenceNotTooBroad.ts, 7, 68)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 8, 24)) +>w : Symbol(w, Decl(returnTypeInferenceNotTooBroad.ts, 8, 27)) +>Wrapper : Symbol(Wrapper, Decl(returnTypeInferenceNotTooBroad.ts, 4, 1)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 8, 24)) +>T : Symbol(T, Decl(returnTypeInferenceNotTooBroad.ts, 8, 24)) + +export const y = sepsis({ low: 1, sign: { kind: 'a', a: 3 }}); +>y : Symbol(y, Decl(returnTypeInferenceNotTooBroad.ts, 9, 12)) +>sepsis : Symbol(sepsis, Decl(returnTypeInferenceNotTooBroad.ts, 6, 1)) +>low : Symbol(low, Decl(returnTypeInferenceNotTooBroad.ts, 9, 25)) +>sign : Symbol(sign, Decl(returnTypeInferenceNotTooBroad.ts, 9, 33)) +>kind : Symbol(kind, Decl(returnTypeInferenceNotTooBroad.ts, 9, 41)) +>a : Symbol(a, Decl(returnTypeInferenceNotTooBroad.ts, 9, 52)) + +// $ExpectType { kind: "a"; a: 3; } +export const yun = unwrap(y); +>yun : Symbol(yun, Decl(returnTypeInferenceNotTooBroad.ts, 11, 12)) +>unwrap : Symbol(unwrap, Decl(returnTypeInferenceNotTooBroad.ts, 7, 68)) +>y : Symbol(y, Decl(returnTypeInferenceNotTooBroad.ts, 9, 12)) + +// $ExpectType { kind: "a"; a: 3; } +export const yone = unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 }})); +>yone : Symbol(yone, Decl(returnTypeInferenceNotTooBroad.ts, 13, 12)) +>unwrap : Symbol(unwrap, Decl(returnTypeInferenceNotTooBroad.ts, 7, 68)) +>sepsis : Symbol(sepsis, Decl(returnTypeInferenceNotTooBroad.ts, 6, 1)) +>low : Symbol(low, Decl(returnTypeInferenceNotTooBroad.ts, 13, 35)) +>sign : Symbol(sign, Decl(returnTypeInferenceNotTooBroad.ts, 13, 43)) +>kind : Symbol(kind, Decl(returnTypeInferenceNotTooBroad.ts, 13, 51)) +>a : Symbol(a, Decl(returnTypeInferenceNotTooBroad.ts, 13, 62)) + diff --git a/tests/baselines/reference/returnTypeInferenceNotTooBroad.types b/tests/baselines/reference/returnTypeInferenceNotTooBroad.types new file mode 100644 index 00000000000..a95d7012d17 --- /dev/null +++ b/tests/baselines/reference/returnTypeInferenceNotTooBroad.types @@ -0,0 +1,65 @@ +=== tests/cases/compiler/returnTypeInferenceNotTooBroad.ts === +type Signs = { kind: 'a'; a: 3; } | { kind: 'b'; b: 2; } | { kind: 'c'; c: 1; }; +>Signs : Signs +>kind : "a" +>a : 3 +>kind : "b" +>b : 2 +>kind : "c" +>c : 1 + +interface Opts { + low?: number; +>low : number + + sign?: T +>sign : T +} +interface Wrapper { +} +declare function sepsis(opts: Opts): Wrapper; +>sepsis : (opts: Opts) => Wrapper +>opts : Opts + +declare function unwrap(w: Wrapper): T; +>unwrap : (w: Wrapper) => T +>w : Wrapper + +export const y = sepsis({ low: 1, sign: { kind: 'a', a: 3 }}); +>y : Wrapper<{ kind: "a"; a: 3; }> +>sepsis({ low: 1, sign: { kind: 'a', a: 3 }}) : Wrapper<{ kind: "a"; a: 3; }> +>sepsis : (opts: Opts) => Wrapper +>{ low: 1, sign: { kind: 'a', a: 3 }} : { low: number; sign: { kind: "a"; a: 3; }; } +>low : number +>1 : 1 +>sign : { kind: "a"; a: 3; } +>{ kind: 'a', a: 3 } : { kind: "a"; a: 3; } +>kind : "a" +>'a' : "a" +>a : 3 +>3 : 3 + +// $ExpectType { kind: "a"; a: 3; } +export const yun = unwrap(y); +>yun : { kind: "a"; a: 3; } +>unwrap(y) : { kind: "a"; a: 3; } +>unwrap : (w: Wrapper) => T +>y : Wrapper<{ kind: "a"; a: 3; }> + +// $ExpectType { kind: "a"; a: 3; } +export const yone = unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 }})); +>yone : { kind: "a"; a: 3; } +>unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 }})) : { kind: "a"; a: 3; } +>unwrap : (w: Wrapper) => T +>sepsis({ low: 1, sign: { kind: 'a', a: 3 }}) : Wrapper<{ kind: "a"; a: 3; }> +>sepsis : (opts: Opts) => Wrapper +>{ low: 1, sign: { kind: 'a', a: 3 }} : { low: number; sign: { kind: "a"; a: 3; }; } +>low : number +>1 : 1 +>sign : { kind: "a"; a: 3; } +>{ kind: 'a', a: 3 } : { kind: "a"; a: 3; } +>kind : "a" +>'a' : "a" +>a : 3 +>3 : 3 + diff --git a/tests/cases/compiler/returnTypeInferenceNotTooBroad.ts b/tests/cases/compiler/returnTypeInferenceNotTooBroad.ts new file mode 100644 index 00000000000..1573b3d72e0 --- /dev/null +++ b/tests/cases/compiler/returnTypeInferenceNotTooBroad.ts @@ -0,0 +1,14 @@ +type Signs = { kind: 'a'; a: 3; } | { kind: 'b'; b: 2; } | { kind: 'c'; c: 1; }; +interface Opts { + low?: number; + sign?: T +} +interface Wrapper { +} +declare function sepsis(opts: Opts): Wrapper; +declare function unwrap(w: Wrapper): T; +export const y = sepsis({ low: 1, sign: { kind: 'a', a: 3 }}); +// $ExpectType { kind: "a"; a: 3; } +export const yun = unwrap(y); +// $ExpectType { kind: "a"; a: 3; } +export const yone = unwrap(sepsis({ low: 1, sign: { kind: 'a', a: 3 }})); \ No newline at end of file