From b8762111118f50ac2f7624a4e8508bff1257c41b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 17:18:43 -0800 Subject: [PATCH] Property handle union/intersection types in type variable checks --- src/compiler/checker.ts | 23 ++++++++++------------- src/compiler/types.ts | 4 +++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81f982e4dc0..dc208f5613e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4523,7 +4523,7 @@ namespace ts { // First, if the constraint type is a type parameter, obtain the base constraint. Then, // if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X. // Finally, iterate over the constituents of the resulting iteration type. - const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; + const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; forEachType(iterationType, t => { // Create a mapper from T to the current iteration type constituent. Then, if the @@ -4579,7 +4579,7 @@ namespace ts { function isGenericMappedType(type: Type) { if (getObjectFlags(type) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(type); - return !!(constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)); + return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index); } return false; } @@ -5912,7 +5912,7 @@ namespace ts { return links.resolvedType; } - function getIndexTypeForTypeVariable(type: TypeVariable) { + function getIndexTypeForGenericType(type: TypeVariable | UnionOrIntersectionType) { if (!type.resolvedIndexType) { type.resolvedIndexType = createType(TypeFlags.Index); type.resolvedIndexType.type = type; @@ -5931,7 +5931,7 @@ namespace ts { } function getIndexType(type: Type): Type { - return type.flags & TypeFlags.TypeVariable ? getIndexTypeForTypeVariable(type) : + return maybeTypeOfKind(type, TypeFlags.TypeVariable) ? getIndexTypeForGenericType(type) : getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(type) : type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromPropertyNames(type); @@ -6032,14 +6032,11 @@ namespace ts { } function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - if (indexType.flags & TypeFlags.TypeVariable || - objectType.flags & TypeFlags.TypeVariable && indexType.flags & TypeFlags.Index || - isGenericMappedType(objectType)) { - // If the object type is a type variable (a type parameter or another indexed access type), if the - // index type is a type variable or an index type, or if the object type is a mapped type with a - // generic constraint, we are performing a higher-order index access where we cannot meaningfully - // access the properties of the object type. In those cases, we first check that the index type is - // assignable to 'keyof T' for the object type. + if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || isGenericMappedType(objectType)) { + // If the index type is generic or if the object type is a mapped type with a generic constraint, + // we are performing a higher-order index access where we cannot meaningfully access the properties + // of the object type. In those cases, we first check that the index type is assignable to 'keyof T' + // for the object type. if (accessNode) { if (!isTypeAssignableTo(indexType, getIndexType(objectType))) { error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType)); @@ -6539,7 +6536,7 @@ namespace ts { // union type A | undefined, we produce { [P in keyof A]: X } | undefined. const constraintType = getConstraintTypeFromMappedType(type); if (constraintType.flags & TypeFlags.Index) { - const typeVariable = (constraintType).type; + const typeVariable = (constraintType).type; const mappedTypeVariable = instantiateType(typeVariable, mapper); if (typeVariable !== mappedTypeVariable) { return mapType(mappedTypeVariable, t => { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aa96b35d758..0ad84d11c35 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2908,6 +2908,8 @@ namespace ts { /* @internal */ resolvedProperties: SymbolTable; // Cache of resolved properties /* @internal */ + resolvedIndexType: IndexType; + /* @internal */ couldContainTypeVariables: boolean; } @@ -2991,7 +2993,7 @@ namespace ts { // keyof T types (TypeFlags.Index) export interface IndexType extends Type { - type: TypeVariable; + type: TypeVariable | UnionOrIntersectionType; } export const enum SignatureKind {