diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ee59b8862bf6..4581ceaf1e263 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14770,17 +14770,25 @@ namespace ts { inferFromTypes(source, (target).falseType); } else if (target.flags & TypeFlags.UnionOrIntersection) { + // We infer from types that are not naked type variables first so that inferences we + // make from nested naked type variables and given slightly higher priority by virtue + // of being first in the candidates array. + for (const t of (target).types) { + if (!getInferenceInfoForType(t)) { + inferFromTypes(source, t); + } + } + // Inferences directly to naked type variables are given lower priority as they are + // less specific. For example, when inferring from Promise to T | Promise, + // we want to infer string for T, not Promise | string. + const savePriority = priority; + priority |= InferencePriority.NakedTypeVariable; for (const t of (target).types) { - const savePriority = priority; - // Inferences directly to naked type variables are given lower priority as they are - // less specific. For example, when inferring from Promise to T | Promise, - // we want to infer string for T, not Promise | string. if (getInferenceInfoForType(t)) { - priority |= InferencePriority.NakedTypeVariable; + inferFromTypes(source, t); } - inferFromTypes(source, t); - priority = savePriority; } + priority = savePriority; } else if (source.flags & TypeFlags.Union) { // Source is a union or intersection type, infer from each constituent type diff --git a/tests/baselines/reference/unionAndIntersectionInference3.js b/tests/baselines/reference/unionAndIntersectionInference3.js new file mode 100644 index 0000000000000..4cc0c2f667e59 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference3.js @@ -0,0 +1,12 @@ +//// [unionAndIntersectionInference3.ts] +// Repro from #30720 + +type Maybe = T | undefined; +declare function concatMaybe(...args: (Maybe | Maybe[])[]): T[]; +concatMaybe([1, 2, 3], 4); + + +//// [unionAndIntersectionInference3.js] +"use strict"; +// Repro from #30720 +concatMaybe([1, 2, 3], 4); diff --git a/tests/baselines/reference/unionAndIntersectionInference3.symbols b/tests/baselines/reference/unionAndIntersectionInference3.symbols new file mode 100644 index 0000000000000..be24bc0cc7781 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference3.symbols @@ -0,0 +1,21 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts === +// Repro from #30720 + +type Maybe = T | undefined; +>Maybe : Symbol(Maybe, Decl(unionAndIntersectionInference3.ts, 0, 0)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 2, 11)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 2, 11)) + +declare function concatMaybe(...args: (Maybe | Maybe[])[]): T[]; +>concatMaybe : Symbol(concatMaybe, Decl(unionAndIntersectionInference3.ts, 2, 30)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 3, 29)) +>args : Symbol(args, Decl(unionAndIntersectionInference3.ts, 3, 32)) +>Maybe : Symbol(Maybe, Decl(unionAndIntersectionInference3.ts, 0, 0)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 3, 29)) +>Maybe : Symbol(Maybe, Decl(unionAndIntersectionInference3.ts, 0, 0)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 3, 29)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 3, 29)) + +concatMaybe([1, 2, 3], 4); +>concatMaybe : Symbol(concatMaybe, Decl(unionAndIntersectionInference3.ts, 2, 30)) + diff --git a/tests/baselines/reference/unionAndIntersectionInference3.types b/tests/baselines/reference/unionAndIntersectionInference3.types new file mode 100644 index 0000000000000..8497cb1e5161f --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference3.types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts === +// Repro from #30720 + +type Maybe = T | undefined; +>Maybe : Maybe + +declare function concatMaybe(...args: (Maybe | Maybe[])[]): T[]; +>concatMaybe : (...args: (T | Maybe[] | undefined)[]) => T[] +>args : (T | Maybe[] | undefined)[] + +concatMaybe([1, 2, 3], 4); +>concatMaybe([1, 2, 3], 4) : number[] +>concatMaybe : (...args: (T | Maybe[] | undefined)[]) => T[] +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 +>4 : 4 + diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts new file mode 100644 index 0000000000000..aad083a626b26 --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts @@ -0,0 +1,7 @@ +// @strict: true + +// Repro from #30720 + +type Maybe = T | undefined; +declare function concatMaybe(...args: (Maybe | Maybe[])[]): T[]; +concatMaybe([1, 2, 3], 4);