mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Don't eagerly simplify reducible generic union index types (#46812)
This commit is contained in:
parent
ea4791d4d7
commit
fc82c67357
@ -814,6 +814,8 @@ namespace ts {
|
||||
|
||||
const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t);
|
||||
const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t);
|
||||
const uniqueLiteralType = createIntrinsicType(TypeFlags.Never, "never"); // `uniqueLiteralType` is a special `never` flagged by union reduction to behave as a literal
|
||||
const uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t); // replace all type parameters with the unique literal type (disregarding constraints)
|
||||
|
||||
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
|
||||
const emptyJsxObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
|
||||
@ -12367,10 +12369,10 @@ namespace ts {
|
||||
else if (type !== firstType) {
|
||||
checkFlags |= CheckFlags.HasNonUniformType;
|
||||
}
|
||||
if (isLiteralType(type) || isPatternLiteralType(type)) {
|
||||
if (isLiteralType(type) || isPatternLiteralType(type) || type === uniqueLiteralType) {
|
||||
checkFlags |= CheckFlags.HasLiteralType;
|
||||
}
|
||||
if (type.flags & TypeFlags.Never) {
|
||||
if (type.flags & TypeFlags.Never && type !== uniqueLiteralType) {
|
||||
checkFlags |= CheckFlags.HasNeverType;
|
||||
}
|
||||
propTypes.push(type);
|
||||
@ -15124,9 +15126,24 @@ namespace ts {
|
||||
/*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations)
|
||||
* must be kept generic, as that instantiation information needs to flow through the type system. By replacing all
|
||||
* type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause the `getReducedType` logic
|
||||
* to reduce the resulting type if possible (since only intersections with conflicting literal-typed properties are reducible).
|
||||
*/
|
||||
function isPossiblyReducibleByInstantiation(type: UnionType): boolean {
|
||||
return some(type.types, t => {
|
||||
const uniqueFilled = getUniqueLiteralFilledInstantiation(t);
|
||||
return getReducedType(uniqueFilled) !== uniqueFilled;
|
||||
});
|
||||
}
|
||||
|
||||
function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean): Type {
|
||||
type = getReducedType(type);
|
||||
return type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
|
||||
return type.flags & TypeFlags.Union ? isPossiblyReducibleByInstantiation(type as UnionType)
|
||||
? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, stringsOnly)
|
||||
: getIntersectionType(map((type as UnionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
|
||||
type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
|
||||
type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) || isGenericMappedType(type) && !hasDistributiveNameType(type) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, stringsOnly) :
|
||||
getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, stringsOnly, noIndexSignatures) :
|
||||
@ -17070,6 +17087,11 @@ namespace ts {
|
||||
return type; // Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable
|
||||
}
|
||||
|
||||
function getUniqueLiteralFilledInstantiation(type: Type) {
|
||||
return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
|
||||
type.uniqueLiteralFilledInstantiation || (type.uniqueLiteralFilledInstantiation = instantiateType(type, uniqueLiteralMapper));
|
||||
}
|
||||
|
||||
function getPermissiveInstantiation(type: Type) {
|
||||
return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
|
||||
type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper));
|
||||
|
||||
@ -5244,6 +5244,8 @@ namespace ts {
|
||||
/* @internal */
|
||||
restrictiveInstantiation?: Type; // Instantiation with type parameters mapped to unconstrained form
|
||||
/* @internal */
|
||||
uniqueLiteralFilledInstantiation?: Type; // Instantiation with type parameters mapped to never type
|
||||
/* @internal */
|
||||
immediateBaseConstraint?: Type; // Immediate base constraint cache
|
||||
/* @internal */
|
||||
widened?: Type; // Cached widened form of the type
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts(33,1): error TS2741: Property 'a' is missing in type 'Gen2<ABC.B>' but required in type 'Gen2<ABC.A>'.
|
||||
tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts(34,1): error TS2741: Property 'b' is missing in type 'Gen2<ABC.A>' but required in type 'Gen2<ABC.B>'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts (2 errors) ====
|
||||
enum ABC { A, B }
|
||||
|
||||
type Gen<T extends ABC> = { v: T; } & (
|
||||
{
|
||||
v: ABC.A,
|
||||
a: string,
|
||||
} | {
|
||||
v: ABC.B,
|
||||
b: string,
|
||||
}
|
||||
)
|
||||
|
||||
// Quick info: ???
|
||||
//
|
||||
// type Gen2<T extends ABC> = {
|
||||
// v: string;
|
||||
// }
|
||||
//
|
||||
type Gen2<T extends ABC> = {
|
||||
[Property in keyof Gen<T>]: string;
|
||||
};
|
||||
|
||||
// 'a' and 'b' properties required !?!?
|
||||
const gen2TypeA: Gen2<ABC.A> = { v: "I am A", a: "" };
|
||||
const gen2TypeB: Gen2<ABC.B> = { v: "I am B", b: "" };
|
||||
|
||||
// 'v' ???
|
||||
type K = keyof Gen2<ABC.A>;
|
||||
|
||||
// :(
|
||||
declare let a: Gen2<ABC.A>;
|
||||
declare let b: Gen2<ABC.B>;
|
||||
a = b;
|
||||
~
|
||||
!!! error TS2741: Property 'a' is missing in type 'Gen2<ABC.B>' but required in type 'Gen2<ABC.A>'.
|
||||
!!! related TS2728 tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts:6:5: 'a' is declared here.
|
||||
b = a;
|
||||
~
|
||||
!!! error TS2741: Property 'b' is missing in type 'Gen2<ABC.A>' but required in type 'Gen2<ABC.B>'.
|
||||
!!! related TS2728 tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts:9:5: 'b' is declared here.
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
//// [mappedTypeNotMistakenlyHomomorphic.ts]
|
||||
enum ABC { A, B }
|
||||
|
||||
type Gen<T extends ABC> = { v: T; } & (
|
||||
{
|
||||
v: ABC.A,
|
||||
a: string,
|
||||
} | {
|
||||
v: ABC.B,
|
||||
b: string,
|
||||
}
|
||||
)
|
||||
|
||||
// Quick info: ???
|
||||
//
|
||||
// type Gen2<T extends ABC> = {
|
||||
// v: string;
|
||||
// }
|
||||
//
|
||||
type Gen2<T extends ABC> = {
|
||||
[Property in keyof Gen<T>]: string;
|
||||
};
|
||||
|
||||
// 'a' and 'b' properties required !?!?
|
||||
const gen2TypeA: Gen2<ABC.A> = { v: "I am A", a: "" };
|
||||
const gen2TypeB: Gen2<ABC.B> = { v: "I am B", b: "" };
|
||||
|
||||
// 'v' ???
|
||||
type K = keyof Gen2<ABC.A>;
|
||||
|
||||
// :(
|
||||
declare let a: Gen2<ABC.A>;
|
||||
declare let b: Gen2<ABC.B>;
|
||||
a = b;
|
||||
b = a;
|
||||
|
||||
|
||||
//// [mappedTypeNotMistakenlyHomomorphic.js]
|
||||
var ABC;
|
||||
(function (ABC) {
|
||||
ABC[ABC["A"] = 0] = "A";
|
||||
ABC[ABC["B"] = 1] = "B";
|
||||
})(ABC || (ABC = {}));
|
||||
// 'a' and 'b' properties required !?!?
|
||||
var gen2TypeA = { v: "I am A", a: "" };
|
||||
var gen2TypeB = { v: "I am B", b: "" };
|
||||
a = b;
|
||||
b = a;
|
||||
@ -0,0 +1,95 @@
|
||||
=== tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts ===
|
||||
enum ABC { A, B }
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>A : Symbol(ABC.A, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 10))
|
||||
>B : Symbol(ABC.B, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 13))
|
||||
|
||||
type Gen<T extends ABC> = { v: T; } & (
|
||||
>Gen : Symbol(Gen, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 17))
|
||||
>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 2, 9))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>v : Symbol(v, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 2, 27))
|
||||
>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 2, 9))
|
||||
{
|
||||
v: ABC.A,
|
||||
>v : Symbol(v, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 3, 3))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>A : Symbol(ABC.A, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 10))
|
||||
|
||||
a: string,
|
||||
>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 4, 13))
|
||||
|
||||
} | {
|
||||
v: ABC.B,
|
||||
>v : Symbol(v, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 6, 7))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>B : Symbol(ABC.B, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 13))
|
||||
|
||||
b: string,
|
||||
>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 7, 13))
|
||||
}
|
||||
)
|
||||
|
||||
// Quick info: ???
|
||||
//
|
||||
// type Gen2<T extends ABC> = {
|
||||
// v: string;
|
||||
// }
|
||||
//
|
||||
type Gen2<T extends ABC> = {
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 18, 10))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
|
||||
[Property in keyof Gen<T>]: string;
|
||||
>Property : Symbol(Property, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 19, 3))
|
||||
>Gen : Symbol(Gen, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 17))
|
||||
>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 18, 10))
|
||||
|
||||
};
|
||||
|
||||
// 'a' and 'b' properties required !?!?
|
||||
const gen2TypeA: Gen2<ABC.A> = { v: "I am A", a: "" };
|
||||
>gen2TypeA : Symbol(gen2TypeA, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 23, 5))
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>A : Symbol(ABC.A, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 10))
|
||||
>v : Symbol(v, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 23, 32))
|
||||
>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 23, 46))
|
||||
|
||||
const gen2TypeB: Gen2<ABC.B> = { v: "I am B", b: "" };
|
||||
>gen2TypeB : Symbol(gen2TypeB, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 24, 5))
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>B : Symbol(ABC.B, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 13))
|
||||
>v : Symbol(v, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 24, 32))
|
||||
>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 24, 46))
|
||||
|
||||
// 'v' ???
|
||||
type K = keyof Gen2<ABC.A>;
|
||||
>K : Symbol(K, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 24, 55))
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>A : Symbol(ABC.A, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 10))
|
||||
|
||||
// :(
|
||||
declare let a: Gen2<ABC.A>;
|
||||
>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 30, 11))
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>A : Symbol(ABC.A, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 10))
|
||||
|
||||
declare let b: Gen2<ABC.B>;
|
||||
>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 31, 11))
|
||||
>Gen2 : Symbol(Gen2, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 10, 1))
|
||||
>ABC : Symbol(ABC, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 0))
|
||||
>B : Symbol(ABC.B, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 0, 13))
|
||||
|
||||
a = b;
|
||||
>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 30, 11))
|
||||
>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 31, 11))
|
||||
|
||||
b = a;
|
||||
>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 31, 11))
|
||||
>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic.ts, 30, 11))
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
=== tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts ===
|
||||
enum ABC { A, B }
|
||||
>ABC : ABC
|
||||
>A : ABC.A
|
||||
>B : ABC.B
|
||||
|
||||
type Gen<T extends ABC> = { v: T; } & (
|
||||
>Gen : Gen<T>
|
||||
>v : T
|
||||
{
|
||||
v: ABC.A,
|
||||
>v : ABC.A
|
||||
>ABC : any
|
||||
|
||||
a: string,
|
||||
>a : string
|
||||
|
||||
} | {
|
||||
v: ABC.B,
|
||||
>v : ABC.B
|
||||
>ABC : any
|
||||
|
||||
b: string,
|
||||
>b : string
|
||||
}
|
||||
)
|
||||
|
||||
// Quick info: ???
|
||||
//
|
||||
// type Gen2<T extends ABC> = {
|
||||
// v: string;
|
||||
// }
|
||||
//
|
||||
type Gen2<T extends ABC> = {
|
||||
>Gen2 : Gen2<T>
|
||||
|
||||
[Property in keyof Gen<T>]: string;
|
||||
};
|
||||
|
||||
// 'a' and 'b' properties required !?!?
|
||||
const gen2TypeA: Gen2<ABC.A> = { v: "I am A", a: "" };
|
||||
>gen2TypeA : Gen2<ABC.A>
|
||||
>ABC : any
|
||||
>{ v: "I am A", a: "" } : { v: string; a: string; }
|
||||
>v : string
|
||||
>"I am A" : "I am A"
|
||||
>a : string
|
||||
>"" : ""
|
||||
|
||||
const gen2TypeB: Gen2<ABC.B> = { v: "I am B", b: "" };
|
||||
>gen2TypeB : Gen2<ABC.B>
|
||||
>ABC : any
|
||||
>{ v: "I am B", b: "" } : { v: string; b: string; }
|
||||
>v : string
|
||||
>"I am B" : "I am B"
|
||||
>b : string
|
||||
>"" : ""
|
||||
|
||||
// 'v' ???
|
||||
type K = keyof Gen2<ABC.A>;
|
||||
>K : "v" | "a"
|
||||
>ABC : any
|
||||
|
||||
// :(
|
||||
declare let a: Gen2<ABC.A>;
|
||||
>a : Gen2<ABC.A>
|
||||
>ABC : any
|
||||
|
||||
declare let b: Gen2<ABC.B>;
|
||||
>b : Gen2<ABC.B>
|
||||
>ABC : any
|
||||
|
||||
a = b;
|
||||
>a = b : Gen2<ABC.B>
|
||||
>a : Gen2<ABC.A>
|
||||
>b : Gen2<ABC.B>
|
||||
|
||||
b = a;
|
||||
>b = a : Gen2<ABC.A>
|
||||
>b : Gen2<ABC.B>
|
||||
>a : Gen2<ABC.A>
|
||||
|
||||
34
tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts
Normal file
34
tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic.ts
Normal file
@ -0,0 +1,34 @@
|
||||
enum ABC { A, B }
|
||||
|
||||
type Gen<T extends ABC> = { v: T; } & (
|
||||
{
|
||||
v: ABC.A,
|
||||
a: string,
|
||||
} | {
|
||||
v: ABC.B,
|
||||
b: string,
|
||||
}
|
||||
)
|
||||
|
||||
// Quick info: ???
|
||||
//
|
||||
// type Gen2<T extends ABC> = {
|
||||
// v: string;
|
||||
// }
|
||||
//
|
||||
type Gen2<T extends ABC> = {
|
||||
[Property in keyof Gen<T>]: string;
|
||||
};
|
||||
|
||||
// 'a' and 'b' properties required !?!?
|
||||
const gen2TypeA: Gen2<ABC.A> = { v: "I am A", a: "" };
|
||||
const gen2TypeB: Gen2<ABC.B> = { v: "I am B", b: "" };
|
||||
|
||||
// 'v' ???
|
||||
type K = keyof Gen2<ABC.A>;
|
||||
|
||||
// :(
|
||||
declare let a: Gen2<ABC.A>;
|
||||
declare let b: Gen2<ABC.B>;
|
||||
a = b;
|
||||
b = a;
|
||||
Loading…
x
Reference in New Issue
Block a user