Introduce MappedType in type checker

This commit is contained in:
Anders Hejlsberg 2016-11-04 14:17:51 -07:00
parent d1a8af5320
commit fc450a2d2f
2 changed files with 107 additions and 14 deletions

View File

@ -2246,6 +2246,9 @@ namespace ts {
else if (getObjectFlags(type) & ObjectFlags.Anonymous) {
writeAnonymousType(<ObjectType>type, nextFlags);
}
else if (getObjectFlags(type) & ObjectFlags.Mapped) {
writeMappedType(<MappedType>type);
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
}
@ -2536,6 +2539,32 @@ namespace ts {
writePunctuation(writer, SyntaxKind.CloseBraceToken);
inObjectTypeLiteral = saveInObjectTypeLiteral;
}
function writeMappedType(type: MappedType) {
const constraintType = getConstraintTypeFromMappedType(type);
if (constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
writePunctuation(writer, SyntaxKind.OpenBracketToken);
appendSymbolNameOnly((type.target || type).typeParameter.symbol, writer);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.InKeyword);
writeSpace(writer);
writeType(constraintType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
else {
writeLiteralType(type, TypeFormatFlags.None);
}
}
}
function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) {
@ -4408,6 +4437,46 @@ namespace ts {
}
}
function forEachType<T>(type: Type, f: (t: Type) => T): T {
return type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, f) : f(type);
}
// { [P in K]: T }
// Get apparent type of K
// If apparent type is a 'keyof T', get apparent type of T
// For each constituent literal type U
// create mapper from P to U
// instantiate T using mapper
// if U is string or number, create index signature with instantiated type
// otherwise create property with name from U and instantiated type
function resolveMappedTypeMembers(type: MappedType) {
const members: SymbolTable = createMap<Symbol>();
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
const target = type.target || type;
const keyType = getApparentType(getConstraintTypeFromMappedType(type));
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
forEachType(iterationType, t => {
const iterationMapper = createUnaryTypeMapper(target.typeParameter, t);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
const propName = (<LiteralType>t).text;
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, propName);
prop.type = instantiateType(target.templateType, templateMapper);
members[propName] = prop;
}
})
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}
function getConstraintTypeFromMappedType(type: MappedType) {
return instantiateType(getConstraintOfTypeParameter((type.target || type).typeParameter), type.mapper || identityMapper);
}
function getTemplateTypeFromMappedType(type: MappedType) {
return instantiateType((type.target || type).templateType, type.mapper || identityMapper);
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
if (!(<ResolvedType>type).members) {
if (type.flags & TypeFlags.Object) {
@ -4420,6 +4489,9 @@ namespace ts {
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
resolveAnonymousTypeMembers(<AnonymousType>type);
}
else if ((<MappedType>type).objectFlags & ObjectFlags.Mapped) {
resolveMappedTypeMembers(<MappedType>type);
}
}
else if (type.flags & TypeFlags.Union) {
resolveUnionTypeMembers(<UnionType>type);
@ -5834,14 +5906,16 @@ namespace ts {
return links.resolvedType;
}
function getTypeFromMappedTypeNode(node: MappedTypeNode) {
function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
getTypeFromTypeNode(node.typeParameter.constraint);
if (node.type) {
getTypeFromTypeNode(node.type);
}
links.resolvedType = unknownType;
const type = <MappedType>createObjectType(ObjectFlags.Mapped);
type.declaration = node;
type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter));
type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
links.resolvedType = type;
}
return links.resolvedType;
}
@ -6015,7 +6089,7 @@ namespace ts {
case SyntaxKind.IndexedAccessType:
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
return getTypeFromMappedTypeNode(<MappedTypeNode>node);
return getTypeFromMappedTypeNode(<MappedTypeNode>node, aliasSymbol, aliasTypeArguments);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
@ -6180,7 +6254,13 @@ namespace ts {
return result;
}
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): ObjectType {
function instantiateAnonymousOrMappedType(type: AnonymousType | MappedType, mapper: TypeMapper): ObjectType {
if (type.objectFlags & ObjectFlags.Instantiated) {
// If the type being instantiated is itself a instantiation, fetch the original target and
// combine the type mappers.
mapper = combineTypeMappers(type.mapper, mapper);
type = type.target;
}
if (mapper.instantiations) {
const cachedType = <ObjectType>mapper.instantiations[type.id];
if (cachedType) {
@ -6191,7 +6271,7 @@ namespace ts {
mapper.instantiations = [];
}
// Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it
const result = <AnonymousType>createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol);
const result = <AnonymousType | MappedType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
result.target = type;
result.mapper = mapper;
result.aliasSymbol = type.aliasSymbol;
@ -6268,7 +6348,10 @@ namespace ts {
return type.symbol &&
type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
((<ObjectType>type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
instantiateAnonymousType(<AnonymousType>type, mapper) : type;
instantiateAnonymousOrMappedType(<AnonymousType>type, mapper) : type;
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return instantiateAnonymousOrMappedType(<MappedType>type, mapper);
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType));

View File

@ -2770,10 +2770,11 @@ namespace ts {
Reference = 1 << 2, // Generic type reference
Tuple = 1 << 3, // Synthesized generic tuple type
Anonymous = 1 << 4, // Anonymous
Instantiated = 1 << 5, // Instantiated anonymous type
ObjectLiteral = 1 << 6, // Originates in an object literal
EvolvingArray = 1 << 7, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties
Mapped = 1 << 5, // Mapped
Instantiated = 1 << 6, // Instantiated anonymous or mapped type
ObjectLiteral = 1 << 7, // Originates in an object literal
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
ClassOrInterface = Class | Interface
}
@ -2842,6 +2843,15 @@ namespace ts {
mapper?: TypeMapper; // Instantiation mapper
}
/* @internal */
export interface MappedType extends ObjectType {
declaration: MappedTypeNode;
typeParameter: TypeParameter;
templateType: Type;
target?: MappedType; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
}
export interface EvolvingArrayType extends ObjectType {
elementType: Type; // Element expressions of evolving array type
finalArrayType?: Type; // Final array type of evolving array type