mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Don't reduce 'keyof M' for mapped types with non-distributive 'as' clauses (#41186)
* Don't reduce 'keyof M' for mapped types with non-distributive as clauses * Add regression test * Accept new baselines
This commit is contained in:
parent
672861abc6
commit
5d021b401a
@ -13475,6 +13475,19 @@ namespace ts {
|
||||
constraint;
|
||||
}
|
||||
|
||||
// Ordinarily we reduce a keyof M where M is a mapped type { [P in K as N<P>]: X } to simply N<K>. This however presumes
|
||||
// that N distributes over union types, i.e. that N<A | B | C> is equivalent to N<A> | N<B> | N<C>. That presumption is
|
||||
// generally true, except when N is a non-distributive conditional type or an instantiable type with non-distributive
|
||||
// conditional type as a constituent. In those cases, we cannot reduce keyof M and need to preserve it as is.
|
||||
function isNonDistributiveNameType(type: Type | undefined): boolean {
|
||||
return !!(type && (
|
||||
type.flags & TypeFlags.Conditional && !(<ConditionalType>type).root.isDistributive ||
|
||||
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) && some((<UnionOrIntersectionType | TemplateLiteralType>type).types, isNonDistributiveNameType) ||
|
||||
type.flags & (TypeFlags.Index | TypeFlags.StringMapping) && isNonDistributiveNameType((<IndexType | StringMappingType>type).type) ||
|
||||
type.flags & TypeFlags.IndexedAccess && isNonDistributiveNameType((<IndexedAccessType>type).indexType) ||
|
||||
type.flags & TypeFlags.Substitution && isNonDistributiveNameType((<SubstitutionType>type).substitute)));
|
||||
}
|
||||
|
||||
function getLiteralTypeFromPropertyName(name: PropertyName) {
|
||||
if (isPrivateIdentifier(name)) {
|
||||
return neverType;
|
||||
@ -13522,7 +13535,7 @@ namespace ts {
|
||||
type = getReducedType(type);
|
||||
return type.flags & TypeFlags.Union ? getIntersectionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
|
||||
type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
|
||||
type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type, stringsOnly) :
|
||||
type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) || isGenericMappedType(type) && isNonDistributiveNameType(getNameTypeFromMappedType(type)) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type, stringsOnly) :
|
||||
getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(<MappedType>type, noIndexSignatures) :
|
||||
type === wildcardType ? wildcardType :
|
||||
type.flags & TypeFlags.Unknown ? neverType :
|
||||
|
||||
@ -57,6 +57,35 @@ const e1: T1 = {
|
||||
};
|
||||
type T2 = keyof T1;
|
||||
const e2: T2 = "foo";
|
||||
|
||||
// Repro from #41133
|
||||
|
||||
interface Car {
|
||||
name: string;
|
||||
seats: number;
|
||||
engine: Engine;
|
||||
wheels: Wheel[];
|
||||
}
|
||||
|
||||
interface Engine {
|
||||
manufacturer: string;
|
||||
horsepower: number;
|
||||
}
|
||||
|
||||
interface Wheel {
|
||||
type: "summer" | "winter";
|
||||
radius: number;
|
||||
}
|
||||
|
||||
type Primitive = string | number | boolean;
|
||||
type OnlyPrimitives<T> = { [K in keyof T as T[K] extends Primitive ? K : never]: T[K] };
|
||||
|
||||
let primitiveCar: OnlyPrimitives<Car>; // { name: string; seats: number; }
|
||||
let keys: keyof OnlyPrimitives<Car>; // "name" | "seats"
|
||||
|
||||
type KeysOfPrimitives<T> = keyof OnlyPrimitives<T>;
|
||||
|
||||
let carKeys: KeysOfPrimitives<Car>; // "name" | "seats"
|
||||
|
||||
|
||||
//// [mappedTypeAsClauses.js]
|
||||
@ -66,6 +95,9 @@ var e1 = {
|
||||
foo: "hello"
|
||||
};
|
||||
var e2 = "foo";
|
||||
var primitiveCar; // { name: string; seats: number; }
|
||||
var keys; // "name" | "seats"
|
||||
var carKeys; // "name" | "seats"
|
||||
|
||||
|
||||
//// [mappedTypeAsClauses.d.ts]
|
||||
@ -135,3 +167,25 @@ declare type T1 = PickByValueType<Example, string>;
|
||||
declare const e1: T1;
|
||||
declare type T2 = keyof T1;
|
||||
declare const e2: T2;
|
||||
interface Car {
|
||||
name: string;
|
||||
seats: number;
|
||||
engine: Engine;
|
||||
wheels: Wheel[];
|
||||
}
|
||||
interface Engine {
|
||||
manufacturer: string;
|
||||
horsepower: number;
|
||||
}
|
||||
interface Wheel {
|
||||
type: "summer" | "winter";
|
||||
radius: number;
|
||||
}
|
||||
declare type Primitive = string | number | boolean;
|
||||
declare type OnlyPrimitives<T> = {
|
||||
[K in keyof T as T[K] extends Primitive ? K : never]: T[K];
|
||||
};
|
||||
declare let primitiveCar: OnlyPrimitives<Car>;
|
||||
declare let keys: keyof OnlyPrimitives<Car>;
|
||||
declare type KeysOfPrimitives<T> = keyof OnlyPrimitives<T>;
|
||||
declare let carKeys: KeysOfPrimitives<Car>;
|
||||
|
||||
@ -188,3 +188,79 @@ const e2: T2 = "foo";
|
||||
>e2 : Symbol(e2, Decl(mappedTypeAsClauses.ts, 57, 5))
|
||||
>T2 : Symbol(T2, Decl(mappedTypeAsClauses.ts, 55, 2))
|
||||
|
||||
// Repro from #41133
|
||||
|
||||
interface Car {
|
||||
>Car : Symbol(Car, Decl(mappedTypeAsClauses.ts, 57, 21))
|
||||
|
||||
name: string;
|
||||
>name : Symbol(Car.name, Decl(mappedTypeAsClauses.ts, 61, 15))
|
||||
|
||||
seats: number;
|
||||
>seats : Symbol(Car.seats, Decl(mappedTypeAsClauses.ts, 62, 17))
|
||||
|
||||
engine: Engine;
|
||||
>engine : Symbol(Car.engine, Decl(mappedTypeAsClauses.ts, 63, 18))
|
||||
>Engine : Symbol(Engine, Decl(mappedTypeAsClauses.ts, 66, 1))
|
||||
|
||||
wheels: Wheel[];
|
||||
>wheels : Symbol(Car.wheels, Decl(mappedTypeAsClauses.ts, 64, 19))
|
||||
>Wheel : Symbol(Wheel, Decl(mappedTypeAsClauses.ts, 71, 1))
|
||||
}
|
||||
|
||||
interface Engine {
|
||||
>Engine : Symbol(Engine, Decl(mappedTypeAsClauses.ts, 66, 1))
|
||||
|
||||
manufacturer: string;
|
||||
>manufacturer : Symbol(Engine.manufacturer, Decl(mappedTypeAsClauses.ts, 68, 18))
|
||||
|
||||
horsepower: number;
|
||||
>horsepower : Symbol(Engine.horsepower, Decl(mappedTypeAsClauses.ts, 69, 25))
|
||||
}
|
||||
|
||||
interface Wheel {
|
||||
>Wheel : Symbol(Wheel, Decl(mappedTypeAsClauses.ts, 71, 1))
|
||||
|
||||
type: "summer" | "winter";
|
||||
>type : Symbol(Wheel.type, Decl(mappedTypeAsClauses.ts, 73, 17))
|
||||
|
||||
radius: number;
|
||||
>radius : Symbol(Wheel.radius, Decl(mappedTypeAsClauses.ts, 74, 30))
|
||||
}
|
||||
|
||||
type Primitive = string | number | boolean;
|
||||
>Primitive : Symbol(Primitive, Decl(mappedTypeAsClauses.ts, 76, 1))
|
||||
|
||||
type OnlyPrimitives<T> = { [K in keyof T as T[K] extends Primitive ? K : never]: T[K] };
|
||||
>OnlyPrimitives : Symbol(OnlyPrimitives, Decl(mappedTypeAsClauses.ts, 78, 43))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 79, 20))
|
||||
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 79, 28))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 79, 20))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 79, 20))
|
||||
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 79, 28))
|
||||
>Primitive : Symbol(Primitive, Decl(mappedTypeAsClauses.ts, 76, 1))
|
||||
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 79, 28))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 79, 20))
|
||||
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 79, 28))
|
||||
|
||||
let primitiveCar: OnlyPrimitives<Car>; // { name: string; seats: number; }
|
||||
>primitiveCar : Symbol(primitiveCar, Decl(mappedTypeAsClauses.ts, 81, 3))
|
||||
>OnlyPrimitives : Symbol(OnlyPrimitives, Decl(mappedTypeAsClauses.ts, 78, 43))
|
||||
>Car : Symbol(Car, Decl(mappedTypeAsClauses.ts, 57, 21))
|
||||
|
||||
let keys: keyof OnlyPrimitives<Car>; // "name" | "seats"
|
||||
>keys : Symbol(keys, Decl(mappedTypeAsClauses.ts, 82, 3))
|
||||
>OnlyPrimitives : Symbol(OnlyPrimitives, Decl(mappedTypeAsClauses.ts, 78, 43))
|
||||
>Car : Symbol(Car, Decl(mappedTypeAsClauses.ts, 57, 21))
|
||||
|
||||
type KeysOfPrimitives<T> = keyof OnlyPrimitives<T>;
|
||||
>KeysOfPrimitives : Symbol(KeysOfPrimitives, Decl(mappedTypeAsClauses.ts, 82, 36))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 84, 22))
|
||||
>OnlyPrimitives : Symbol(OnlyPrimitives, Decl(mappedTypeAsClauses.ts, 78, 43))
|
||||
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 84, 22))
|
||||
|
||||
let carKeys: KeysOfPrimitives<Car>; // "name" | "seats"
|
||||
>carKeys : Symbol(carKeys, Decl(mappedTypeAsClauses.ts, 86, 3))
|
||||
>KeysOfPrimitives : Symbol(KeysOfPrimitives, Decl(mappedTypeAsClauses.ts, 82, 36))
|
||||
>Car : Symbol(Car, Decl(mappedTypeAsClauses.ts, 57, 21))
|
||||
|
||||
|
||||
@ -120,3 +120,53 @@ const e2: T2 = "foo";
|
||||
>e2 : "foo"
|
||||
>"foo" : "foo"
|
||||
|
||||
// Repro from #41133
|
||||
|
||||
interface Car {
|
||||
name: string;
|
||||
>name : string
|
||||
|
||||
seats: number;
|
||||
>seats : number
|
||||
|
||||
engine: Engine;
|
||||
>engine : Engine
|
||||
|
||||
wheels: Wheel[];
|
||||
>wheels : Wheel[]
|
||||
}
|
||||
|
||||
interface Engine {
|
||||
manufacturer: string;
|
||||
>manufacturer : string
|
||||
|
||||
horsepower: number;
|
||||
>horsepower : number
|
||||
}
|
||||
|
||||
interface Wheel {
|
||||
type: "summer" | "winter";
|
||||
>type : "summer" | "winter"
|
||||
|
||||
radius: number;
|
||||
>radius : number
|
||||
}
|
||||
|
||||
type Primitive = string | number | boolean;
|
||||
>Primitive : Primitive
|
||||
|
||||
type OnlyPrimitives<T> = { [K in keyof T as T[K] extends Primitive ? K : never]: T[K] };
|
||||
>OnlyPrimitives : OnlyPrimitives<T>
|
||||
|
||||
let primitiveCar: OnlyPrimitives<Car>; // { name: string; seats: number; }
|
||||
>primitiveCar : OnlyPrimitives<Car>
|
||||
|
||||
let keys: keyof OnlyPrimitives<Car>; // "name" | "seats"
|
||||
>keys : "name" | "seats"
|
||||
|
||||
type KeysOfPrimitives<T> = keyof OnlyPrimitives<T>;
|
||||
>KeysOfPrimitives : keyof OnlyPrimitives<T>
|
||||
|
||||
let carKeys: KeysOfPrimitives<Car>; // "name" | "seats"
|
||||
>carKeys : "name" | "seats"
|
||||
|
||||
|
||||
@ -59,3 +59,32 @@ const e1: T1 = {
|
||||
};
|
||||
type T2 = keyof T1;
|
||||
const e2: T2 = "foo";
|
||||
|
||||
// Repro from #41133
|
||||
|
||||
interface Car {
|
||||
name: string;
|
||||
seats: number;
|
||||
engine: Engine;
|
||||
wheels: Wheel[];
|
||||
}
|
||||
|
||||
interface Engine {
|
||||
manufacturer: string;
|
||||
horsepower: number;
|
||||
}
|
||||
|
||||
interface Wheel {
|
||||
type: "summer" | "winter";
|
||||
radius: number;
|
||||
}
|
||||
|
||||
type Primitive = string | number | boolean;
|
||||
type OnlyPrimitives<T> = { [K in keyof T as T[K] extends Primitive ? K : never]: T[K] };
|
||||
|
||||
let primitiveCar: OnlyPrimitives<Car>; // { name: string; seats: number; }
|
||||
let keys: keyof OnlyPrimitives<Car>; // "name" | "seats"
|
||||
|
||||
type KeysOfPrimitives<T> = keyof OnlyPrimitives<T>;
|
||||
|
||||
let carKeys: KeysOfPrimitives<Car>; // "name" | "seats"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user