Defer keyof (T & U) for generic T and/or U

This commit is contained in:
Anders Hejlsberg
2023-12-03 09:24:53 -08:00
parent 3258d75169
commit 5866644c7c
+15 -3
View File
@@ -17702,7 +17702,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
isGenericTupleType(type) ||
isGenericMappedType(type) && !hasDistributiveNameType(type) ||
type.flags & TypeFlags.Union && !(indexFlags & IndexFlags.NoReducibleCheck) && isGenericReducibleType(type) ||
type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType));
type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable));
}
function getIndexType(type: Type, indexFlags = defaultIndexFlags): Type {
@@ -18215,14 +18215,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getSimplifiedType(type: Type, writing: boolean): Type {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type as IndexedAccessType, writing) :
type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type as ConditionalType, writing) :
type.flags & TypeFlags.Index ? getSimplifiedIndexType(type as IndexType) :
type;
}
function distributeIndexOverObjectType(objectType: Type, indexType: Type, writing: boolean) {
// (T | U)[K] -> T[K] | U[K] (reading)
// (T | U)[K] -> T[K] & U[K] (writing)
// (T & U)[K] -> T[K] & U[K]
if (objectType.flags & TypeFlags.Union || objectType.flags & TypeFlags.Intersection && !shouldDeferIndexType(objectType)) {
// (T & U)[K] -> T[K] & U[K], except when T or U is {}
if (objectType.flags & TypeFlags.Union || objectType.flags & TypeFlags.Intersection && !(maybeTypeOfKind(objectType, TypeFlags.Instantiable) && some((objectType as IntersectionType).types, isEmptyAnonymousObjectType))) {
const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType), writing));
return objectType.flags & TypeFlags.Intersection || writing ? getIntersectionType(types) : getUnionType(types);
}
@@ -18314,6 +18315,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return type;
}
function getSimplifiedIndexType(type: IndexType) {
// keyof (T & U) -> keyof T | keyof U, except when T or U is {}
const target = type.type;
return target.flags & TypeFlags.Intersection && !some((target as IntersectionType).types, isEmptyAnonymousObjectType) ?
getUnionType(map((target as IntersectionType).types, t => getIndexType(t, type.indexFlags))) :
type;
}
/**
* Invokes union simplification logic to determine if an intersection is considered empty as a union constituent
*/
@@ -25656,6 +25665,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function inferToMappedType(source: Type, target: MappedType, constraintType: Type): boolean {
if (constraintType.flags & TypeFlags.Index) {
constraintType = getSimplifiedIndexType(constraintType as IndexType);
}
if (constraintType.flags & TypeFlags.Union) {
let result = false;
for (const type of (constraintType as UnionType).types) {