diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 680147a5b6f..40e5a890f2e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5697,63 +5697,80 @@ namespace ts { return links.resolvedType; } - function createIndexedAccessType(objectType: Type, keyType: TypeParameter) { + function createIndexedAccessType(objectType: Type, indexType: TypeParameter) { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; - type.indexType = keyType; + type.indexType = indexType; return type; } - function getIndexedAccessTypeForTypeParameter(objectType: Type, keyType: TypeParameter) { - const indexedAccessTypes = keyType.resolvedIndexedAccessTypes || (keyType.resolvedIndexedAccessTypes = []); - return indexedAccessTypes[objectType.id] || (indexedAccessTypes[objectType.id] = createIndexedAccessType(objectType, keyType)); + function getIndexedAccessTypeForTypeParameter(objectType: Type, indexType: TypeParameter) { + const indexedAccessTypes = indexType.resolvedIndexedAccessTypes || (indexType.resolvedIndexedAccessTypes = []); + return indexedAccessTypes[objectType.id] || (indexedAccessTypes[objectType.id] = createIndexedAccessType(objectType, indexType)); } - function getPropertyTypeForIndexType(objectType: Type, indexType: Type) { - return indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) && getTypeOfPropertyOfType(objectType, escapeIdentifier((indexType).text)) || - isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) && getIndexTypeOfType(objectType, IndexKind.Number) || - isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike) && getIndexTypeOfType(objectType, IndexKind.String) || - undefined; + function getPropertyTypeForIndexType(objectType: Type, indexType: Type, errorNode?: Node) { + if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { + const propType = getTypeOfPropertyOfType(objectType, escapeIdentifier((indexType).text)); + if (propType) { + return propType; + } + } + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike)) { + const numberIndexType = getIndexTypeOfType(objectType, IndexKind.Number); + if (numberIndexType) { + return numberIndexType; + } + } + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike)) { + const stringIndexType = getIndexTypeOfType(objectType, IndexKind.String); + if (stringIndexType) { + return stringIndexType; + } + } + if (errorNode) { + if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { + error(errorNode, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType).text, typeToString(objectType)); + } + else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) { + error(errorNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(indexType)); + } + else { + error(errorNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType)); + } + } + return unknownType; } - function getIndexedAccessType(objectType: Type, keyType: Type) { - return keyType.flags & TypeFlags.Any ? anyType : - keyType.flags & TypeFlags.TypeParameter ? getIndexedAccessTypeForTypeParameter(objectType, keyType) : - mapType(keyType, t => getPropertyTypeForIndexType(objectType, t) || unknownType); - } - - function resolveIndexedAccessTypeNode(node: IndexedAccessTypeNode) { - const objectType = getTypeFromTypeNodeNoAlias(node.objectType); - const indexType = getTypeFromTypeNodeNoAlias(node.indexType); + function getIndexedAccessType(objectType: Type, indexType: Type, errorNode?: Node) { if (indexType.flags & TypeFlags.TypeParameter) { if (!isTypeAssignableTo(getConstraintOfTypeParameter(indexType), getIndexType(objectType))) { - error(node.indexType, Diagnostics.Type_0_is_not_constrained_to_keyof_1, typeToString(indexType), typeToString(objectType)); - return unknownType; - } - return getIndexedAccessType(objectType, indexType); - } - const indexTypes = indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive) ? (indexType).types : [indexType]; - for (const t of indexTypes) { - if (!getPropertyTypeForIndexType(objectType, t)) { - if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { - error(node.indexType, Diagnostics.Property_0_does_not_exist_on_type_1, (t).text, typeToString(objectType)) - } - else if (t.flags & (TypeFlags.String | TypeFlags.Number)) { - error(node.indexType, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(t)); - } - else { - error(node.indexType, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(t)); + if (errorNode) { + error(errorNode, Diagnostics.Type_0_is_not_constrained_to_keyof_1, typeToString(indexType), typeToString(objectType)); } return unknownType; } + return getIndexedAccessTypeForTypeParameter(objectType, indexType); } - return getIndexedAccessType(objectType, indexType); + if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) { + const propTypes: Type[] = []; + for (const t of (indexType).types) { + const propType = getPropertyTypeForIndexType(objectType, t, errorNode); + if (propType === unknownType) { + return unknownType; + } + propTypes.push(propType); + } + return getUnionType(propTypes); + } + return getPropertyTypeForIndexType(objectType, indexType, errorNode); } function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = resolveIndexedAccessTypeNode(node); + links.resolvedType = getIndexedAccessType(getTypeFromTypeNodeNoAlias(node.objectType), + getTypeFromTypeNodeNoAlias(node.indexType), node.indexType); } return links.resolvedType; }