mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-27 13:42:16 -05:00
Initial implementation of 'T[K]' property access types
This commit is contained in:
@@ -3067,6 +3067,7 @@ namespace ts {
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.ThisType:
|
||||
case SyntaxKind.TypeOperator:
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
case SyntaxKind.LiteralType:
|
||||
// Types and signatures are TypeScript syntax, and exclude all other facts.
|
||||
transformFlags = TransformFlags.AssertTypeScript;
|
||||
|
||||
@@ -2222,7 +2222,13 @@ namespace ts {
|
||||
else if (type.flags & TypeFlags.PropertyName) {
|
||||
writer.writeKeyword("keyof");
|
||||
writeSpace(writer);
|
||||
writeType((<PropertyNameType>type).type, TypeFormatFlags.None);
|
||||
writeType((<PropertyNameType>type).type, TypeFormatFlags.InElementType);
|
||||
}
|
||||
else if (type.flags & TypeFlags.PropertyAccess) {
|
||||
writeType((<PropertyAccessType>type).objectType, TypeFormatFlags.InElementType);
|
||||
writePunctuation(writer, SyntaxKind.OpenBracketToken);
|
||||
writeType((<PropertyAccessType>type).keyType, TypeFormatFlags.None);
|
||||
writePunctuation(writer, SyntaxKind.CloseBracketToken);
|
||||
}
|
||||
else {
|
||||
// Should never get here
|
||||
@@ -5694,6 +5700,55 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function createPropertyAccessType(objectType: Type, keyType: TypeParameter) {
|
||||
const type = <PropertyAccessType>createType(TypeFlags.PropertyAccess);
|
||||
type.objectType = objectType;
|
||||
type.keyType = 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 getPropertyAccessType(objectType: Type, keyType: Type) {
|
||||
if (keyType.flags & TypeFlags.TypeParameter) {
|
||||
return getPropertyAccessTypeForTypeParameter(objectType, <TypeParameter>keyType);
|
||||
}
|
||||
if (isTypeOfKind(keyType, TypeFlags.StringLiteral) && !(keyType.flags & TypeFlags.Intersection)) {
|
||||
return mapType(keyType, t => getTypeOfPropertyOfType(objectType, escapeIdentifier((<LiteralType>t).text)) || unknownType);
|
||||
}
|
||||
return keyType.flags & TypeFlags.Any ? anyType : unknownType;
|
||||
}
|
||||
|
||||
function resolvePropertyAccessTypeNode(node: PropertyAccessTypeNode) {
|
||||
const objectType = getTypeFromTypeNodeNoAlias(node.objectType);
|
||||
const keyType = getTypeFromTypeNodeNoAlias(node.keyType);
|
||||
if (keyType.flags & TypeFlags.TypeParameter &&
|
||||
getConstraintOfTypeParameter(<TypeParameter>keyType) === getPropertyNameType(objectType)) {
|
||||
return getPropertyAccessType(objectType, keyType);
|
||||
}
|
||||
if (isTypeOfKind(keyType, TypeFlags.StringLiteral) && !(keyType.flags & TypeFlags.Intersection)) {
|
||||
const missing = forEachType(keyType, t => getTypeOfPropertyOfType(objectType, escapeIdentifier((<LiteralType>t).text)) ? undefined : (<LiteralType>t).text);
|
||||
if (missing) {
|
||||
error(node.keyType, Diagnostics.Property_0_is_missing_in_type_1, missing, typeToString(objectType));
|
||||
return unknownType;
|
||||
}
|
||||
return getPropertyAccessType(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));
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
function getTypeFromPropertyAccessTypeNode(node: PropertyAccessTypeNode) {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = resolvePropertyAccessTypeNode(node);
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
@@ -5855,6 +5910,8 @@ namespace ts {
|
||||
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments);
|
||||
case SyntaxKind.TypeOperator:
|
||||
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
return getTypeFromPropertyAccessTypeNode(<PropertyAccessTypeNode>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:
|
||||
@@ -6119,6 +6176,9 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.PropertyName) {
|
||||
return getPropertyNameType(instantiateType((<PropertyNameType>type).type, mapper));
|
||||
}
|
||||
if (type.flags & TypeFlags.PropertyAccess) {
|
||||
return getPropertyAccessType(instantiateType((<PropertyAccessType>type).objectType, mapper), instantiateType((<PropertyAccessType>type).keyType, mapper));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@@ -8054,7 +8114,7 @@ namespace ts {
|
||||
|
||||
function hasPrimitiveConstraint(type: TypeParameter): boolean {
|
||||
const constraint = getConstraintOfTypeParameter(type);
|
||||
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive);
|
||||
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.PropertyName);
|
||||
}
|
||||
|
||||
function getInferredType(context: InferenceContext, index: number): Type {
|
||||
@@ -8546,6 +8606,10 @@ namespace ts {
|
||||
return containsType(target.types, source);
|
||||
}
|
||||
|
||||
function forEachType<T>(type: Type, f: (t: Type) => T): T {
|
||||
return type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, f) : f(type);
|
||||
}
|
||||
|
||||
function filterType(type: Type, f: (t: Type) => boolean): Type {
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
const types = (<UnionType>type).types;
|
||||
|
||||
@@ -415,6 +415,8 @@ namespace ts {
|
||||
return emitParenType(<ParenthesizedTypeNode>type);
|
||||
case SyntaxKind.TypeOperator:
|
||||
return emitTypeOperator(<TypeOperatorNode>type);
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
return emitPropertyAccessType(<PropertyAccessTypeNode>type);
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
|
||||
@@ -514,6 +516,13 @@ namespace ts {
|
||||
emitType(type.type);
|
||||
}
|
||||
|
||||
function emitPropertyAccessType(node: PropertyAccessTypeNode) {
|
||||
emitType(node.objectType);
|
||||
write("[");
|
||||
emitType(node.keyType);
|
||||
write("]");
|
||||
}
|
||||
|
||||
function emitTypeLiteral(type: TypeLiteralNode) {
|
||||
write("{");
|
||||
if (type.members.length) {
|
||||
|
||||
@@ -1751,6 +1751,10 @@
|
||||
"category": "Error",
|
||||
"code": 2535
|
||||
},
|
||||
"Property access element type must be a string literal type or a type parameter constrained to 'keyof {0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2536
|
||||
},
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
"code": 2600
|
||||
|
||||
@@ -596,6 +596,8 @@ const _super = (function (geti, seti) {
|
||||
return emitThisType();
|
||||
case SyntaxKind.TypeOperator:
|
||||
return emitTypeOperator(<TypeOperatorNode>node);
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
return emitPropertyAccessType(<PropertyAccessTypeNode>node);
|
||||
case SyntaxKind.LiteralType:
|
||||
return emitLiteralType(<LiteralTypeNode>node);
|
||||
|
||||
@@ -1096,6 +1098,13 @@ const _super = (function (geti, seti) {
|
||||
emit(node.type);
|
||||
}
|
||||
|
||||
function emitPropertyAccessType(node: PropertyAccessTypeNode) {
|
||||
emit(node.objectType);
|
||||
write("[");
|
||||
emit(node.keyType);
|
||||
write("]");
|
||||
}
|
||||
|
||||
function emitLiteralType(node: LiteralTypeNode) {
|
||||
emitExpression(node.literal);
|
||||
}
|
||||
|
||||
@@ -136,6 +136,9 @@ namespace ts {
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.TypeOperator:
|
||||
return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type);
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
return visitNode(cbNode, (<PropertyAccessTypeNode>node).objectType) ||
|
||||
visitNode(cbNode, (<PropertyAccessTypeNode>node).keyType);
|
||||
case SyntaxKind.LiteralType:
|
||||
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
@@ -2519,10 +2522,19 @@ namespace ts {
|
||||
function parseArrayTypeOrHigher(): TypeNode {
|
||||
let type = parseNonArrayType();
|
||||
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
const node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
|
||||
node.elementType = type;
|
||||
type = finishNode(node);
|
||||
if (isStartOfType()) {
|
||||
const node = <PropertyAccessTypeNode>createNode(SyntaxKind.PropertyAccessType, type.pos);
|
||||
node.objectType = type;
|
||||
node.keyType = parseType();
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
type = finishNode(node);
|
||||
}
|
||||
else {
|
||||
const node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
|
||||
node.elementType = type;
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
type = finishNode(node);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -289,6 +289,7 @@ namespace ts {
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.ThisType:
|
||||
case SyntaxKind.TypeOperator:
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
case SyntaxKind.LiteralType:
|
||||
// TypeScript type nodes are elided.
|
||||
|
||||
@@ -1858,6 +1859,7 @@ namespace ts {
|
||||
// Fallthrough
|
||||
case SyntaxKind.TypeQuery:
|
||||
case SyntaxKind.TypeOperator:
|
||||
case SyntaxKind.PropertyAccessType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.ThisType:
|
||||
|
||||
@@ -218,6 +218,7 @@ namespace ts {
|
||||
ParenthesizedType,
|
||||
ThisType,
|
||||
TypeOperator,
|
||||
PropertyAccessType,
|
||||
LiteralType,
|
||||
// Binding patterns
|
||||
ObjectBindingPattern,
|
||||
@@ -878,6 +879,12 @@ namespace ts {
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface PropertyAccessTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.PropertyAccessType;
|
||||
objectType: TypeNode;
|
||||
keyType: TypeNode;
|
||||
}
|
||||
|
||||
export interface LiteralTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.LiteralType;
|
||||
literal: Expression;
|
||||
@@ -2624,14 +2631,15 @@ namespace ts {
|
||||
Union = 1 << 16, // Union (T | U)
|
||||
Intersection = 1 << 17, // Intersection (T & U)
|
||||
PropertyName = 1 << 18, // keyof T
|
||||
PropertyAccess = 1 << 19, // T[K]
|
||||
/* @internal */
|
||||
FreshLiteral = 1 << 19, // Fresh literal type
|
||||
FreshLiteral = 1 << 20, // Fresh literal type
|
||||
/* @internal */
|
||||
ContainsWideningType = 1 << 20, // Type is or contains undefined or null widening type
|
||||
ContainsWideningType = 1 << 21, // Type is or contains undefined or null widening type
|
||||
/* @internal */
|
||||
ContainsObjectLiteral = 1 << 21, // Type is or contains object literal type
|
||||
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
ContainsAnyFunctionType = 1 << 22, // Type is or contains object literal type
|
||||
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
|
||||
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
@@ -2654,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 | StringLike | NumberLike | BooleanLike | ESSymbol,
|
||||
Narrowable = Any | StructuredType | TypeParameter | PropertyAccess | StringLike | NumberLike | BooleanLike | ESSymbol,
|
||||
NotUnionOrUnit = Any | ESSymbol | Object,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
|
||||
@@ -2819,6 +2827,8 @@ namespace ts {
|
||||
/* @internal */
|
||||
resolvedPropertyNameType: PropertyNameType;
|
||||
/* @internal */
|
||||
resolvedPropertyAccessTypes: PropertyAccessType[];
|
||||
/* @internal */
|
||||
isThisType?: boolean;
|
||||
}
|
||||
|
||||
@@ -2826,6 +2836,11 @@ namespace ts {
|
||||
type: TypeParameter;
|
||||
}
|
||||
|
||||
export interface PropertyAccessType extends Type {
|
||||
objectType: Type;
|
||||
keyType: TypeParameter;
|
||||
}
|
||||
|
||||
export const enum SignatureKind {
|
||||
Call,
|
||||
Construct,
|
||||
|
||||
Reference in New Issue
Block a user