diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5318595aeca..c05d0135f14 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3067,7 +3067,7 @@ namespace ts { case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.ThisType: case SyntaxKind.TypeOperator: - case SyntaxKind.PropertyAccessType: + case SyntaxKind.IndexedAccessType: case SyntaxKind.LiteralType: // Types and signatures are TypeScript syntax, and exclude all other facts. transformFlags = TransformFlags.AssertTypeScript; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f18c795ae3..0d23fbb44a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2219,15 +2219,15 @@ namespace ts { else if (type.flags & TypeFlags.StringOrNumberLiteral) { writer.writeStringLiteral(literalTypeToString(type)); } - else if (type.flags & TypeFlags.PropertyName) { + else if (type.flags & TypeFlags.Index) { writer.writeKeyword("keyof"); writeSpace(writer); - writeType((type).type, TypeFormatFlags.InElementType); + writeType((type).type, TypeFormatFlags.InElementType); } - else if (type.flags & TypeFlags.PropertyAccess) { - writeType((type).objectType, TypeFormatFlags.InElementType); + else if (type.flags & TypeFlags.IndexedAccess) { + writeType((type).objectType, TypeFormatFlags.InElementType); writePunctuation(writer, SyntaxKind.OpenBracketToken); - writeType((type).keyType, TypeFormatFlags.None); + writeType((type).indexType, TypeFormatFlags.None); writePunctuation(writer, SyntaxKind.CloseBracketToken); } else { @@ -5678,43 +5678,43 @@ namespace ts { return startsWith(prop.name, "__@") ? neverType : getLiteralTypeForText(TypeFlags.StringLiteral, unescapeIdentifier(prop.name)); } - function getPropertyNameTypeForTypeParameter(type: TypeParameter) { - if (!type.resolvedPropertyNameType) { - type.resolvedPropertyNameType = createType(TypeFlags.PropertyName); - type.resolvedPropertyNameType.type = type; + function getIndexTypeForTypeParameter(type: TypeParameter) { + if (!type.resolvedIndexType) { + type.resolvedIndexType = createType(TypeFlags.Index); + type.resolvedIndexType.type = type; } - return type.resolvedPropertyNameType; + return type.resolvedIndexType; } - function getPropertyNameType(type: Type): Type { + function getIndexType(type: Type): Type { return type.flags & TypeFlags.TypeParameter ? - getPropertyNameTypeForTypeParameter(type) : + getIndexTypeForTypeParameter(type) : getUnionType(map(getPropertiesOfType(type), getLiteralTypeFromPropertyName)); } function getTypeFromTypeOperatorNode(node: TypeOperatorNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getPropertyNameType(getTypeFromTypeNodeNoAlias(node.type)); + links.resolvedType = getIndexType(getTypeFromTypeNodeNoAlias(node.type)); } return links.resolvedType; } - function createPropertyAccessType(objectType: Type, keyType: TypeParameter) { - const type = createType(TypeFlags.PropertyAccess); + function createIndexedAccessType(objectType: Type, keyType: TypeParameter) { + const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; - type.keyType = keyType; + type.indexType = keyType; return type; } - function getPropertyAccessTypeForTypeParameter(objectType: Type, keyType: TypeParameter) { - const propertyAccessTypes = keyType.resolvedPropertyAccessTypes || (keyType.resolvedPropertyAccessTypes = []); - return propertyAccessTypes[objectType.id] || (propertyAccessTypes[objectType.id] = createPropertyAccessType(objectType, keyType)); + function getIndexedAccessTypeForTypeParameter(objectType: Type, keyType: TypeParameter) { + const indexedAccessTypes = keyType.resolvedIndexedAccessTypes || (keyType.resolvedIndexedAccessTypes = []); + return indexedAccessTypes[objectType.id] || (indexedAccessTypes[objectType.id] = createIndexedAccessType(objectType, keyType)); } - function getPropertyAccessType(objectType: Type, keyType: Type) { + function getIndexedAccessType(objectType: Type, keyType: Type) { if (keyType.flags & TypeFlags.TypeParameter) { - return getPropertyAccessTypeForTypeParameter(objectType, keyType); + return getIndexedAccessTypeForTypeParameter(objectType, keyType); } if (isTypeOfKind(keyType, TypeFlags.StringLiteral) && !(keyType.flags & TypeFlags.Intersection)) { return mapType(keyType, t => getTypeOfPropertyOfType(objectType, escapeIdentifier((t).text)) || unknownType); @@ -5722,29 +5722,29 @@ namespace ts { return keyType.flags & TypeFlags.Any ? anyType : unknownType; } - function resolvePropertyAccessTypeNode(node: PropertyAccessTypeNode) { + function resolveIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const objectType = getTypeFromTypeNodeNoAlias(node.objectType); - const keyType = getTypeFromTypeNodeNoAlias(node.keyType); + const keyType = getTypeFromTypeNodeNoAlias(node.indexType); if (keyType.flags & TypeFlags.TypeParameter && - getConstraintOfTypeParameter(keyType) === getPropertyNameType(objectType)) { - return getPropertyAccessType(objectType, keyType); + getConstraintOfTypeParameter(keyType) === getIndexType(objectType)) { + return getIndexedAccessType(objectType, keyType); } if (isTypeOfKind(keyType, TypeFlags.StringLiteral) && !(keyType.flags & TypeFlags.Intersection)) { const missing = forEachType(keyType, t => getTypeOfPropertyOfType(objectType, escapeIdentifier((t).text)) ? undefined : (t).text); if (missing) { - error(node.keyType, Diagnostics.Property_0_is_missing_in_type_1, missing, typeToString(objectType)); + error(node.indexType, Diagnostics.Property_0_is_missing_in_type_1, missing, typeToString(objectType)); return unknownType; } - return getPropertyAccessType(objectType, keyType); + return getIndexedAccessType(objectType, keyType); } - error(node.keyType, Diagnostics.Property_access_element_type_must_be_a_string_literal_type_or_a_type_parameter_constrained_to_keyof_0, typeToString(objectType)); + error(node.indexType, Diagnostics.Property_access_element_type_must_be_a_string_literal_type_or_a_type_parameter_constrained_to_keyof_0, typeToString(objectType)); return unknownType; } - function getTypeFromPropertyAccessTypeNode(node: PropertyAccessTypeNode) { + function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = resolvePropertyAccessTypeNode(node); + links.resolvedType = resolveIndexedAccessTypeNode(node); } return links.resolvedType; } @@ -5910,8 +5910,8 @@ namespace ts { return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); case SyntaxKind.TypeOperator: return getTypeFromTypeOperatorNode(node); - case SyntaxKind.PropertyAccessType: - return getTypeFromPropertyAccessTypeNode(node); + case SyntaxKind.IndexedAccessType: + return getTypeFromIndexedAccessTypeNode(node); // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: @@ -6173,11 +6173,11 @@ namespace ts { if (type.flags & TypeFlags.Intersection) { return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes); } - if (type.flags & TypeFlags.PropertyName) { - return getPropertyNameType(instantiateType((type).type, mapper)); + if (type.flags & TypeFlags.Index) { + return getIndexType(instantiateType((type).type, mapper)); } - if (type.flags & TypeFlags.PropertyAccess) { - return getPropertyAccessType(instantiateType((type).objectType, mapper), instantiateType((type).keyType, mapper)); + if (type.flags & TypeFlags.IndexedAccess) { + return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); } } return type; @@ -8114,7 +8114,7 @@ namespace ts { function hasPrimitiveConstraint(type: TypeParameter): boolean { const constraint = getConstraintOfTypeParameter(type); - return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.PropertyName); + return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index); } function getInferredType(context: InferenceContext, index: number): Type { @@ -11520,14 +11520,20 @@ namespace ts { } } - // Obtain base constraint such that we can bail out if the constraint is an unknown type - const objectType = getApparentType(checkNonNullExpression(node.expression)); + let objectType = checkNonNullExpression(node.expression); const indexType = node.argumentExpression ? checkExpression(node.argumentExpression) : unknownType; if (objectType === unknownType || objectType === silentNeverType) { return objectType; } + if (indexType.flags & TypeFlags.TypeParameter && + isTypeAssignableTo(getConstraintOfTypeParameter(indexType), getIndexType(objectType))) { + return getIndexedAccessType(objectType, indexType); + } + + objectType = getApparentType(objectType); + const isConstEnum = isConstEnumObjectType(objectType); if (isConstEnum && (!node.argumentExpression || node.argumentExpression.kind !== SyntaxKind.StringLiteral)) { @@ -15043,6 +15049,10 @@ namespace ts { forEach(node.types, checkSourceElement); } + function checkIndexedAccessType(node: IndexedAccessTypeNode) { + getTypeFromIndexedAccessTypeNode(node); + } + function isPrivateWithinAmbient(node: Node): boolean { return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node); } @@ -18327,6 +18337,8 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return checkSourceElement((node).type); + case SyntaxKind.IndexedAccessType: + return checkIndexedAccessType(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 271e020c389..0bba375a2cb 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -415,8 +415,8 @@ namespace ts { return emitParenType(type); case SyntaxKind.TypeOperator: return emitTypeOperator(type); - case SyntaxKind.PropertyAccessType: - return emitPropertyAccessType(type); + case SyntaxKind.IndexedAccessType: + return emitPropertyAccessType(type); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: return emitSignatureDeclarationWithJsDocComments(type); @@ -516,10 +516,10 @@ namespace ts { emitType(type.type); } - function emitPropertyAccessType(node: PropertyAccessTypeNode) { + function emitPropertyAccessType(node: IndexedAccessTypeNode) { emitType(node.objectType); write("["); - emitType(node.keyType); + emitType(node.indexType); write("]"); } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index dde921765df..f29ef0f4cf1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -596,8 +596,8 @@ const _super = (function (geti, seti) { return emitThisType(); case SyntaxKind.TypeOperator: return emitTypeOperator(node); - case SyntaxKind.PropertyAccessType: - return emitPropertyAccessType(node); + case SyntaxKind.IndexedAccessType: + return emitPropertyAccessType(node); case SyntaxKind.LiteralType: return emitLiteralType(node); @@ -1098,10 +1098,10 @@ const _super = (function (geti, seti) { emit(node.type); } - function emitPropertyAccessType(node: PropertyAccessTypeNode) { + function emitPropertyAccessType(node: IndexedAccessTypeNode) { emit(node.objectType); write("["); - emit(node.keyType); + emit(node.indexType); write("]"); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8fadb259184..7b09327c1b8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -136,9 +136,9 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return visitNode(cbNode, (node).type); - case SyntaxKind.PropertyAccessType: - return visitNode(cbNode, (node).objectType) || - visitNode(cbNode, (node).keyType); + case SyntaxKind.IndexedAccessType: + return visitNode(cbNode, (node).objectType) || + visitNode(cbNode, (node).indexType); case SyntaxKind.LiteralType: return visitNode(cbNode, (node).literal); case SyntaxKind.ObjectBindingPattern: @@ -2523,9 +2523,9 @@ namespace ts { let type = parseNonArrayType(); while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { if (isStartOfType()) { - const node = createNode(SyntaxKind.PropertyAccessType, type.pos); + const node = createNode(SyntaxKind.IndexedAccessType, type.pos); node.objectType = type; - node.keyType = parseType(); + node.indexType = parseType(); parseExpected(SyntaxKind.CloseBracketToken); type = finishNode(node); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 6b0c92e3c8e..188364e0bd7 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -289,7 +289,7 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.ThisType: case SyntaxKind.TypeOperator: - case SyntaxKind.PropertyAccessType: + case SyntaxKind.IndexedAccessType: case SyntaxKind.LiteralType: // TypeScript type nodes are elided. @@ -1859,7 +1859,7 @@ namespace ts { // Fallthrough case SyntaxKind.TypeQuery: case SyntaxKind.TypeOperator: - case SyntaxKind.PropertyAccessType: + case SyntaxKind.IndexedAccessType: case SyntaxKind.TypeLiteral: case SyntaxKind.AnyKeyword: case SyntaxKind.ThisType: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3912c6e4642..54aabcc6a81 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -218,7 +218,7 @@ namespace ts { ParenthesizedType, ThisType, TypeOperator, - PropertyAccessType, + IndexedAccessType, LiteralType, // Binding patterns ObjectBindingPattern, @@ -879,10 +879,10 @@ namespace ts { type: TypeNode; } - export interface PropertyAccessTypeNode extends TypeNode { - kind: SyntaxKind.PropertyAccessType; + export interface IndexedAccessTypeNode extends TypeNode { + kind: SyntaxKind.IndexedAccessType; objectType: TypeNode; - keyType: TypeNode; + indexType: TypeNode; } export interface LiteralTypeNode extends TypeNode { @@ -2630,8 +2630,8 @@ namespace ts { Object = 1 << 15, // Object type Union = 1 << 16, // Union (T | U) Intersection = 1 << 17, // Intersection (T & U) - PropertyName = 1 << 18, // keyof T - PropertyAccess = 1 << 19, // T[K] + Index = 1 << 18, // keyof T + IndexedAccess = 1 << 19, // T[K] /* @internal */ FreshLiteral = 1 << 20, // Fresh literal type /* @internal */ @@ -2652,7 +2652,7 @@ namespace ts { Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never, /* @internal */ Primitive = String | Number | Boolean | Enum | ESSymbol | Void | Undefined | Null | Literal, - StringLike = String | StringLiteral | PropertyName, + StringLike = String | StringLiteral | Index, NumberLike = Number | NumberLiteral | Enum | EnumLiteral, BooleanLike = Boolean | BooleanLiteral, EnumLike = Enum | EnumLiteral, @@ -2662,7 +2662,7 @@ namespace ts { // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never - Narrowable = Any | StructuredType | TypeParameter | PropertyAccess | StringLike | NumberLike | BooleanLike | ESSymbol, + Narrowable = Any | StructuredType | TypeParameter | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol, NotUnionOrUnit = Any | ESSymbol | Object, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectLiteral, @@ -2825,20 +2825,20 @@ namespace ts { /* @internal */ resolvedApparentType: Type; /* @internal */ - resolvedPropertyNameType: PropertyNameType; + resolvedIndexType: IndexType; /* @internal */ - resolvedPropertyAccessTypes: PropertyAccessType[]; + resolvedIndexedAccessTypes: IndexedAccessType[]; /* @internal */ isThisType?: boolean; } - export interface PropertyNameType extends Type { + export interface IndexType extends Type { type: TypeParameter; } - export interface PropertyAccessType extends Type { + export interface IndexedAccessType extends Type { objectType: Type; - keyType: TypeParameter; + indexType: TypeParameter; } export const enum SignatureKind {