diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3cc7ff57f8cba..e6f367cf46c39 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14720,6 +14720,13 @@ namespace ts { } return !!((type).objectFlags & ObjectFlags.IsGenericObjectType); } + if (type.flags & TypeFlags.Substitution) { + if (!((type).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) { + (type).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed | + (isGenericObjectType((type).substitute) || isGenericObjectType((type).baseType) ? ObjectFlags.IsGenericObjectType : 0); + } + return !!((type).objectFlags & ObjectFlags.IsGenericObjectType); + } return !!(type.flags & TypeFlags.InstantiableNonPrimitive) || isGenericMappedType(type) || isGenericTupleType(type); } @@ -14731,6 +14738,13 @@ namespace ts { } return !!((type).objectFlags & ObjectFlags.IsGenericIndexType); } + if (type.flags & TypeFlags.Substitution) { + if (!((type).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) { + (type).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed | + (isGenericIndexType((type).substitute) || isGenericIndexType((type).baseType) ? ObjectFlags.IsGenericIndexType : 0); + } + return !!((type).objectFlags & ObjectFlags.IsGenericIndexType); + } return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type); } @@ -21128,6 +21142,13 @@ namespace ts { inferFromTypes((source).type, (target).type); } } + else if (source.flags & TypeFlags.Substitution) { + inferFromTypes((source as SubstitutionType).baseType, target); + const oldPriority = priority; + priority |= InferencePriority.SubstituteSource; + inferFromTypes((source as SubstitutionType).substitute, target); // Make substitute inference at a lower priority + priority = oldPriority; + } else if (target.flags & TypeFlags.Conditional) { invokeOnce(source, target, inferToConditionalType); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0dda734aedf85..780050afed5b5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5201,7 +5201,7 @@ namespace ts { /* @internal */ IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type - // Flags that require TypeFlags.UnionOrIntersection + // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution /* @internal */ IsGenericObjectTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed /* @internal */ @@ -5553,6 +5553,7 @@ namespace ts { // Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution // types disappear upon instantiation (just like type parameters). export interface SubstitutionType extends InstantiableType { + objectFlags: ObjectFlags; baseType: Type; // Target type substitute: Type; // Type to substitute for type parameter } @@ -5663,15 +5664,16 @@ namespace ts { export const enum InferencePriority { NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type SpeculativeTuple = 1 << 1, // Speculative tuple inference - HomomorphicMappedType = 1 << 2, // Reverse inference for homomorphic mapped type - PartialHomomorphicMappedType = 1 << 3, // Partial reverse inference for homomorphic mapped type - MappedTypeConstraint = 1 << 4, // Reverse inference for mapped type - ContravariantConditional = 1 << 5, // Conditional type in contravariant position - ReturnType = 1 << 6, // Inference made from return type of generic function - LiteralKeyof = 1 << 7, // Inference made from a string literal to a keyof T - NoConstraints = 1 << 8, // Don't infer from constraints of instantiable types - AlwaysStrict = 1 << 9, // Always use strict rules for contravariant inferences - MaxValue = 1 << 10, // Seed for inference priority tracking + SubstituteSource = 1 << 2, // Source of inference originated within a substitution type's substitute + HomomorphicMappedType = 1 << 3, // Reverse inference for homomorphic mapped type + PartialHomomorphicMappedType = 1 << 4, // Partial reverse inference for homomorphic mapped type + MappedTypeConstraint = 1 << 5, // Reverse inference for mapped type + ContravariantConditional = 1 << 6, // Conditional type in contravariant position + ReturnType = 1 << 7, // Inference made from return type of generic function + LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T + NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types + AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences + MaxValue = 1 << 11, // Seed for inference priority tracking PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates Circularity = -1, // Inference circularity (value less than all other priorities) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 29cb071ef82bc..cb272a9c68992 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2681,6 +2681,7 @@ declare namespace ts { type: Type; } export interface SubstitutionType extends InstantiableType { + objectFlags: ObjectFlags; baseType: Type; substitute: Type; } @@ -2705,16 +2706,17 @@ declare namespace ts { export enum InferencePriority { NakedTypeVariable = 1, SpeculativeTuple = 2, - HomomorphicMappedType = 4, - PartialHomomorphicMappedType = 8, - MappedTypeConstraint = 16, - ContravariantConditional = 32, - ReturnType = 64, - LiteralKeyof = 128, - NoConstraints = 256, - AlwaysStrict = 512, - MaxValue = 1024, - PriorityImpliesCombination = 208, + SubstituteSource = 4, + HomomorphicMappedType = 8, + PartialHomomorphicMappedType = 16, + MappedTypeConstraint = 32, + ContravariantConditional = 64, + ReturnType = 128, + LiteralKeyof = 256, + NoConstraints = 512, + AlwaysStrict = 1024, + MaxValue = 2048, + PriorityImpliesCombination = 416, Circularity = -1 } /** @deprecated Use FileExtensionInfo instead. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 95d0e8ad937bc..ba45ef005512c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2681,6 +2681,7 @@ declare namespace ts { type: Type; } export interface SubstitutionType extends InstantiableType { + objectFlags: ObjectFlags; baseType: Type; substitute: Type; } @@ -2705,16 +2706,17 @@ declare namespace ts { export enum InferencePriority { NakedTypeVariable = 1, SpeculativeTuple = 2, - HomomorphicMappedType = 4, - PartialHomomorphicMappedType = 8, - MappedTypeConstraint = 16, - ContravariantConditional = 32, - ReturnType = 64, - LiteralKeyof = 128, - NoConstraints = 256, - AlwaysStrict = 512, - MaxValue = 1024, - PriorityImpliesCombination = 208, + SubstituteSource = 4, + HomomorphicMappedType = 8, + PartialHomomorphicMappedType = 16, + MappedTypeConstraint = 32, + ContravariantConditional = 64, + ReturnType = 128, + LiteralKeyof = 256, + NoConstraints = 512, + AlwaysStrict = 1024, + MaxValue = 2048, + PriorityImpliesCombination = 416, Circularity = -1 } /** @deprecated Use FileExtensionInfo instead. */ diff --git a/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.js b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.js new file mode 100644 index 0000000000000..dbdd00ed8f7dc --- /dev/null +++ b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.js @@ -0,0 +1,6 @@ +//// [nongenericConditionalNotPartiallyComputed.ts] +// Expected: type A = number +// Got: type A = number[] extends (infer T)[] ? T : never +type A = Array extends Array ? Array extends Array ? T : never : never + +//// [nongenericConditionalNotPartiallyComputed.js] diff --git a/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.symbols b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.symbols new file mode 100644 index 0000000000000..4c210e6fcba26 --- /dev/null +++ b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/nongenericConditionalNotPartiallyComputed.ts === +// Expected: type A = number +// Got: type A = number[] extends (infer T)[] ? T : never +type A = Array extends Array ? Array extends Array ? T : never : never +>A : Symbol(A, Decl(nongenericConditionalNotPartiallyComputed.ts, 0, 0)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(nongenericConditionalNotPartiallyComputed.ts, 2, 77)) +>T : Symbol(T, Decl(nongenericConditionalNotPartiallyComputed.ts, 2, 77)) + diff --git a/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.types b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.types new file mode 100644 index 0000000000000..d69a5dca9f54c --- /dev/null +++ b/tests/baselines/reference/nongenericConditionalNotPartiallyComputed.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/nongenericConditionalNotPartiallyComputed.ts === +// Expected: type A = number +// Got: type A = number[] extends (infer T)[] ? T : never +type A = Array extends Array ? Array extends Array ? T : never : never +>A : number + diff --git a/tests/cases/compiler/nongenericConditionalNotPartiallyComputed.ts b/tests/cases/compiler/nongenericConditionalNotPartiallyComputed.ts new file mode 100644 index 0000000000000..11ee8cf883f4a --- /dev/null +++ b/tests/cases/compiler/nongenericConditionalNotPartiallyComputed.ts @@ -0,0 +1,3 @@ +// Expected: type A = number +// Got: type A = number[] extends (infer T)[] ? T : never +type A = Array extends Array ? Array extends Array ? T : never : never \ No newline at end of file