Skip to content

Compute dervied generic-ness for substitutions and create lower priority inferences for substitutes #43765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14720,6 +14720,13 @@ namespace ts {
}
return !!((<UnionOrIntersectionType>type).objectFlags & ObjectFlags.IsGenericObjectType);
}
if (type.flags & TypeFlags.Substitution) {
if (!((<SubstitutionType>type).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
(<SubstitutionType>type).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
(isGenericObjectType((<SubstitutionType>type).substitute) || isGenericObjectType((<SubstitutionType>type).baseType) ? ObjectFlags.IsGenericObjectType : 0);
}
return !!((<SubstitutionType>type).objectFlags & ObjectFlags.IsGenericObjectType);
}
return !!(type.flags & TypeFlags.InstantiableNonPrimitive) || isGenericMappedType(type) || isGenericTupleType(type);
}

Expand All @@ -14731,6 +14738,13 @@ namespace ts {
}
return !!((<UnionOrIntersectionType>type).objectFlags & ObjectFlags.IsGenericIndexType);
}
if (type.flags & TypeFlags.Substitution) {
if (!((<SubstitutionType>type).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
(<SubstitutionType>type).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
(isGenericIndexType((<SubstitutionType>type).substitute) || isGenericIndexType((<SubstitutionType>type).baseType) ? ObjectFlags.IsGenericIndexType : 0);
}
return !!((<SubstitutionType>type).objectFlags & ObjectFlags.IsGenericIndexType);
}
return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type);
}

Expand Down Expand Up @@ -21128,6 +21142,13 @@ namespace ts {
inferFromTypes((<StringMappingType>source).type, (<StringMappingType>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);
}
Expand Down
22 changes: 12 additions & 10 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
22 changes: 12 additions & 10 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2681,6 +2681,7 @@ declare namespace ts {
type: Type;
}
export interface SubstitutionType extends InstantiableType {
objectFlags: ObjectFlags;
baseType: Type;
substitute: Type;
}
Expand All @@ -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. */
Expand Down
22 changes: 12 additions & 10 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2681,6 +2681,7 @@ declare namespace ts {
type: Type;
}
export interface SubstitutionType extends InstantiableType {
objectFlags: ObjectFlags;
baseType: Type;
substitute: Type;
}
Expand All @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//// [nongenericConditionalNotPartiallyComputed.ts]
// Expected: type A = number
// Got: type A = number[] extends (infer T)[] ? T : never
type A = Array<number> extends Array<any> ? Array<number> extends Array<infer T> ? T : never : never

//// [nongenericConditionalNotPartiallyComputed.js]
Original file line number Diff line number Diff line change
@@ -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<number> extends Array<any> ? Array<number> extends Array<infer T> ? 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))

Original file line number Diff line number Diff line change
@@ -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<number> extends Array<any> ? Array<number> extends Array<infer T> ? T : never : never
>A : number

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Expected: type A = number
// Got: type A = number[] extends (infer T)[] ? T : never
type A = Array<number> extends Array<any> ? Array<number> extends Array<infer T> ? T : never : never