Support parametric property access expressions + some renaming

This commit is contained in:
Anders Hejlsberg 2016-10-26 13:39:12 -07:00
parent c21592ede1
commit e7cfbfe185
7 changed files with 80 additions and 68 deletions

View File

@ -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;

View File

@ -2219,15 +2219,15 @@ namespace ts {
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
}
else if (type.flags & TypeFlags.PropertyName) {
else if (type.flags & TypeFlags.Index) {
writer.writeKeyword("keyof");
writeSpace(writer);
writeType((<PropertyNameType>type).type, TypeFormatFlags.InElementType);
writeType((<IndexType>type).type, TypeFormatFlags.InElementType);
}
else if (type.flags & TypeFlags.PropertyAccess) {
writeType((<PropertyAccessType>type).objectType, TypeFormatFlags.InElementType);
else if (type.flags & TypeFlags.IndexedAccess) {
writeType((<IndexedAccessType>type).objectType, TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeType((<PropertyAccessType>type).keyType, TypeFormatFlags.None);
writeType((<IndexedAccessType>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 = <PropertyNameType>createType(TypeFlags.PropertyName);
type.resolvedPropertyNameType.type = type;
function getIndexTypeForTypeParameter(type: TypeParameter) {
if (!type.resolvedIndexType) {
type.resolvedIndexType = <IndexType>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(<TypeParameter>type) :
getIndexTypeForTypeParameter(<TypeParameter>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 = <PropertyAccessType>createType(TypeFlags.PropertyAccess);
function createIndexedAccessType(objectType: Type, keyType: TypeParameter) {
const type = <IndexedAccessType>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, <TypeParameter>keyType);
return getIndexedAccessTypeForTypeParameter(objectType, <TypeParameter>keyType);
}
if (isTypeOfKind(keyType, TypeFlags.StringLiteral) && !(keyType.flags & TypeFlags.Intersection)) {
return mapType(keyType, t => getTypeOfPropertyOfType(objectType, escapeIdentifier((<LiteralType>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(<TypeParameter>keyType) === getPropertyNameType(objectType)) {
return getPropertyAccessType(objectType, keyType);
getConstraintOfTypeParameter(<TypeParameter>keyType) === getIndexType(objectType)) {
return getIndexedAccessType(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));
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(<TypeOperatorNode>node);
case SyntaxKind.PropertyAccessType:
return getTypeFromPropertyAccessTypeNode(<PropertyAccessTypeNode>node);
case SyntaxKind.IndexedAccessType:
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>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((<IntersectionType>type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes);
}
if (type.flags & TypeFlags.PropertyName) {
return getPropertyNameType(instantiateType((<PropertyNameType>type).type, mapper));
if (type.flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (type.flags & TypeFlags.PropertyAccess) {
return getPropertyAccessType(instantiateType((<PropertyAccessType>type).objectType, mapper), instantiateType((<PropertyAccessType>type).keyType, mapper));
if (type.flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>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(<TypeParameter>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((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
case SyntaxKind.IndexedAccessType:
return checkIndexedAccessType(<IndexedAccessTypeNode>node);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:

View File

@ -415,8 +415,8 @@ namespace ts {
return emitParenType(<ParenthesizedTypeNode>type);
case SyntaxKind.TypeOperator:
return emitTypeOperator(<TypeOperatorNode>type);
case SyntaxKind.PropertyAccessType:
return emitPropertyAccessType(<PropertyAccessTypeNode>type);
case SyntaxKind.IndexedAccessType:
return emitPropertyAccessType(<IndexedAccessTypeNode>type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>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("]");
}

View File

@ -596,8 +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.IndexedAccessType:
return emitPropertyAccessType(<IndexedAccessTypeNode>node);
case SyntaxKind.LiteralType:
return emitLiteralType(<LiteralTypeNode>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("]");
}

View File

@ -136,9 +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.IndexedAccessType:
return visitNode(cbNode, (<IndexedAccessTypeNode>node).objectType) ||
visitNode(cbNode, (<IndexedAccessTypeNode>node).indexType);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
case SyntaxKind.ObjectBindingPattern:
@ -2523,9 +2523,9 @@ namespace ts {
let type = parseNonArrayType();
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
if (isStartOfType()) {
const node = <PropertyAccessTypeNode>createNode(SyntaxKind.PropertyAccessType, type.pos);
const node = <IndexedAccessTypeNode>createNode(SyntaxKind.IndexedAccessType, type.pos);
node.objectType = type;
node.keyType = parseType();
node.indexType = parseType();
parseExpected(SyntaxKind.CloseBracketToken);
type = finishNode(node);
}

View File

@ -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:

View File

@ -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 {