mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Initial implementation of 'readonly' type operator
This commit is contained in:
+80
-15
@@ -2490,13 +2490,18 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.Index) {
|
||||
const indexedType = (<IndexType>type).type;
|
||||
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
|
||||
return createTypeOperatorNode(indexTypeNode);
|
||||
return createTypeOperatorNode(SyntaxKind.KeyOfKeyword, indexTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
|
||||
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
|
||||
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.Readonly || objectFlags & ObjectFlags.Readonly) {
|
||||
const readonlyType = (<ReadonlyTypeVariable | ReadonlyObjectType>type).type;
|
||||
const readonlyTypeNode = typeToTypeNodeHelper(readonlyType, context);
|
||||
return createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, readonlyTypeNode);
|
||||
}
|
||||
|
||||
Debug.fail("Should be unreachable.");
|
||||
|
||||
@@ -3247,13 +3252,13 @@ namespace ts {
|
||||
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
|
||||
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
|
||||
}
|
||||
else if (type.flags & TypeFlags.Index) {
|
||||
else if (type.flags & (TypeFlags.Index | TypeFlags.Readonly) || getObjectFlags(type) & ObjectFlags.Readonly) {
|
||||
if (flags & TypeFormatFlags.InElementType) {
|
||||
writePunctuation(writer, SyntaxKind.OpenParenToken);
|
||||
}
|
||||
writer.writeKeyword("keyof");
|
||||
writer.writeKeyword(type.flags & TypeFlags.Index ? "keyof" : "readonly");
|
||||
writeSpace(writer);
|
||||
writeType((<IndexType>type).type, TypeFormatFlags.InElementType);
|
||||
writeType((<IndexType | ReadonlyTypeVariable>type).type, TypeFormatFlags.InElementType);
|
||||
if (flags & TypeFormatFlags.InElementType) {
|
||||
writePunctuation(writer, SyntaxKind.CloseParenToken);
|
||||
}
|
||||
@@ -4676,7 +4681,7 @@ namespace ts {
|
||||
if (!popTypeResolution()) {
|
||||
type = reportCircularityError(symbol);
|
||||
}
|
||||
links.type = type;
|
||||
links.type = getCheckFlags(symbol) & CheckFlags.ReadonlyType ? getReadonlyType(type) : type;
|
||||
}
|
||||
}
|
||||
return links.type;
|
||||
@@ -5666,6 +5671,23 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createReadonlyIndexInfo(indexInfo: IndexInfo): IndexInfo {
|
||||
return indexInfo && !indexInfo.isReadonly ? createIndexInfo(indexInfo.type, true, indexInfo.declaration) : indexInfo;
|
||||
}
|
||||
|
||||
function resolveReadonlyTypeMembers(type: ReadonlyObjectType) {
|
||||
const target = type.type;
|
||||
const members = createMap<Symbol>() as SymbolTable;
|
||||
for (const symbol of getPropertiesOfObjectType(target)) {
|
||||
members.set(symbol.name, instantiateSymbol(symbol, identityMapper, /*readonly*/ true));
|
||||
}
|
||||
const callSignatures = getSignaturesOfType(target, SignatureKind.Call);
|
||||
const constructSignatures = getSignaturesOfType(target, SignatureKind.Construct);
|
||||
const stringIndexInfo = createReadonlyIndexInfo(getIndexInfoOfType(target, IndexKind.String));
|
||||
const numberIndexInfo = createReadonlyIndexInfo(getIndexInfoOfType(target, IndexKind.Number));
|
||||
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
|
||||
}
|
||||
|
||||
/** Resolve the members of a mapped type { [P in K]: T } */
|
||||
function resolveMappedTypeMembers(type: MappedType) {
|
||||
const members: SymbolTable = createSymbolTable();
|
||||
@@ -5747,7 +5769,7 @@ namespace ts {
|
||||
function getModifiersTypeFromMappedType(type: MappedType) {
|
||||
if (!type.modifiersType) {
|
||||
const constraintDeclaration = type.declaration.typeParameter.constraint;
|
||||
if (constraintDeclaration.kind === SyntaxKind.TypeOperator) {
|
||||
if (constraintDeclaration.kind === SyntaxKind.TypeOperator && (<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
|
||||
// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
|
||||
// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
|
||||
// 'keyof T' to a literal union type and we can't recover T from that type.
|
||||
@@ -5777,16 +5799,20 @@ namespace ts {
|
||||
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
|
||||
if (!(<ResolvedType>type).members) {
|
||||
if (type.flags & TypeFlags.Object) {
|
||||
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
|
||||
const objectFlags = (<ObjectType>type).objectFlags;
|
||||
if (objectFlags & ObjectFlags.Reference) {
|
||||
resolveTypeReferenceMembers(<TypeReference>type);
|
||||
}
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.ClassOrInterface) {
|
||||
else if (objectFlags & ObjectFlags.ClassOrInterface) {
|
||||
resolveClassOrInterfaceMembers(<InterfaceType>type);
|
||||
}
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
|
||||
else if (objectFlags & ObjectFlags.Anonymous) {
|
||||
resolveAnonymousTypeMembers(<AnonymousType>type);
|
||||
}
|
||||
else if ((<MappedType>type).objectFlags & ObjectFlags.Mapped) {
|
||||
else if (objectFlags & ObjectFlags.Readonly) {
|
||||
resolveReadonlyTypeMembers(<ReadonlyObjectType>type);
|
||||
}
|
||||
else if (objectFlags & ObjectFlags.Mapped) {
|
||||
resolveMappedTypeMembers(<MappedType>type);
|
||||
}
|
||||
}
|
||||
@@ -5875,7 +5901,8 @@ namespace ts {
|
||||
function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
|
||||
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
|
||||
type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(<IndexedAccessType>type) :
|
||||
getBaseConstraintOfType(type);
|
||||
type.flags & TypeFlags.Readonly ? getConstraintOfReadonlyTypeVariable(<ReadonlyTypeVariable>type) :
|
||||
getBaseConstraintOfType(type);
|
||||
}
|
||||
|
||||
function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
|
||||
@@ -5888,6 +5915,11 @@ namespace ts {
|
||||
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
|
||||
}
|
||||
|
||||
function getConstraintOfReadonlyTypeVariable(type: ReadonlyTypeVariable) {
|
||||
const baseType = getBaseConstraintOfType(type.type);
|
||||
return baseType ? getReadonlyType(baseType) : undefined;
|
||||
}
|
||||
|
||||
function getBaseConstraintOfType(type: Type): Type {
|
||||
if (type.flags & (TypeFlags.TypeVariable | TypeFlags.UnionOrIntersection)) {
|
||||
const constraint = getResolvedBaseConstraint(<TypeVariable | UnionOrIntersectionType>type);
|
||||
@@ -5959,6 +5991,10 @@ namespace ts {
|
||||
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
|
||||
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
|
||||
}
|
||||
if (t.flags & TypeFlags.Readonly) {
|
||||
const baseConstraint = getBaseConstraint((<ReadonlyTypeVariable>t).type);
|
||||
return baseConstraint ? getReadonlyType(baseConstraint) : undefined;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -7478,10 +7514,36 @@ namespace ts {
|
||||
return indexType !== neverType ? indexType : stringType;
|
||||
}
|
||||
|
||||
function createReadonlyObjectType(type: ObjectType): ReadonlyObjectType {
|
||||
const result = <ReadonlyObjectType>createObjectType(ObjectFlags.Readonly, type.symbol);
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
function createReadonlyTypeVariable(type: TypeVariable): ReadonlyTypeVariable {
|
||||
const result = <ReadonlyTypeVariable>createType(TypeFlags.Readonly);
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
function getReadonlyType(type: Type): Type {
|
||||
if (!(type.flags & TypeFlags.HasReadonlyForm)) {
|
||||
return type;
|
||||
}
|
||||
if (!type.readonlyType) {
|
||||
type.readonlyType = type.flags & TypeFlags.Object ? createReadonlyObjectType(<ObjectType>type) :
|
||||
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getReadonlyType)) :
|
||||
type.flags & TypeFlags.Intersection ? getIntersectionType(sameMap((<IntersectionType>type).types, getReadonlyType)) :
|
||||
createReadonlyTypeVariable(<TypeVariable>type);
|
||||
}
|
||||
return type.readonlyType;
|
||||
}
|
||||
|
||||
function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = getIndexType(getTypeFromTypeNode(node.type));
|
||||
const type = getTypeFromTypeNode(node.type);
|
||||
links.resolvedType = node.operator === SyntaxKind.KeyOfKeyword ? getIndexType(type) : getReadonlyType(type);
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
@@ -8006,6 +8068,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
|
||||
if (mapper1 === identityMapper) return mapper2;
|
||||
if (mapper2 === identityMapper) return mapper1;
|
||||
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
|
||||
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
|
||||
return mapper;
|
||||
@@ -8068,8 +8132,9 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol {
|
||||
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
|
||||
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper, readonly?: boolean): Symbol {
|
||||
const checkFlags = getCheckFlags(symbol);
|
||||
if (checkFlags & CheckFlags.Instantiated) {
|
||||
const links = getSymbolLinks(symbol);
|
||||
// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
|
||||
// type mappers. This ensures that original type identities are properly preserved and that aliases
|
||||
@@ -8080,7 +8145,7 @@ namespace ts {
|
||||
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
|
||||
// also transient so that we can just store data on it directly.
|
||||
const result = createSymbol(symbol.flags, symbol.name);
|
||||
result.checkFlags = CheckFlags.Instantiated;
|
||||
result.checkFlags = CheckFlags.Instantiated | (readonly || checkFlags & CheckFlags.ReadonlyType ? CheckFlags.Readonly | CheckFlags.ReadonlyType : 0);
|
||||
result.declarations = symbol.declarations;
|
||||
result.parent = symbol.parent;
|
||||
result.target = symbol;
|
||||
|
||||
@@ -627,15 +627,15 @@ namespace ts {
|
||||
return <ThisTypeNode>createSynthesizedNode(SyntaxKind.ThisType);
|
||||
}
|
||||
|
||||
export function createTypeOperatorNode(type: TypeNode) {
|
||||
export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode) {
|
||||
const node = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode;
|
||||
node.operator = SyntaxKind.KeyOfKeyword;
|
||||
node.operator = operator;
|
||||
node.type = parenthesizeElementTypeMember(type);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode) {
|
||||
return node.type !== type ? updateNode(createTypeOperatorNode(type), node) : node;
|
||||
return node.type !== type ? updateNode(createTypeOperatorNode(node.operator, type), node) : node;
|
||||
}
|
||||
|
||||
export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) {
|
||||
|
||||
@@ -2653,7 +2653,7 @@ namespace ts {
|
||||
return type;
|
||||
}
|
||||
|
||||
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword) {
|
||||
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword) {
|
||||
const node = <TypeOperatorNode>createNode(SyntaxKind.TypeOperator);
|
||||
parseExpected(operator);
|
||||
node.operator = operator;
|
||||
@@ -2662,9 +2662,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseTypeOperatorOrHigher(): TypeNode {
|
||||
switch (token()) {
|
||||
const operator = token();
|
||||
switch (operator) {
|
||||
case SyntaxKind.KeyOfKeyword:
|
||||
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
return parseTypeOperator(operator);
|
||||
}
|
||||
return parseArrayTypeOrHigher();
|
||||
}
|
||||
|
||||
+28
-13
@@ -986,7 +986,7 @@ namespace ts {
|
||||
|
||||
export interface TypeOperatorNode extends TypeNode {
|
||||
kind: SyntaxKind.TypeOperator;
|
||||
operator: SyntaxKind.KeyOfKeyword;
|
||||
operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
@@ -3023,6 +3023,7 @@ namespace ts {
|
||||
ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s)
|
||||
ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s)
|
||||
ContainsStatic = 1 << 9, // Synthetic property with static constituent(s)
|
||||
ReadonlyType = 1 << 10, // Obtain readonly form of type
|
||||
Synthetic = SyntheticProperty | SyntheticMethod
|
||||
}
|
||||
|
||||
@@ -3157,17 +3158,18 @@ namespace ts {
|
||||
Intersection = 1 << 17, // Intersection (T & U)
|
||||
Index = 1 << 18, // keyof T
|
||||
IndexedAccess = 1 << 19, // T[K]
|
||||
Readonly = 1 << 20, // readonly T
|
||||
/* @internal */
|
||||
FreshLiteral = 1 << 20, // Fresh literal type
|
||||
FreshLiteral = 1 << 21, // Fresh literal type
|
||||
/* @internal */
|
||||
ContainsWideningType = 1 << 21, // Type is or contains undefined or null widening type
|
||||
ContainsWideningType = 1 << 22, // Type is or contains undefined or null widening type
|
||||
/* @internal */
|
||||
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
|
||||
ContainsObjectLiteral = 1 << 23, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
|
||||
NonPrimitive = 1 << 24, // intrinsic object type
|
||||
ContainsAnyFunctionType = 1 << 24, // Type is or contains object literal type
|
||||
NonPrimitive = 1 << 25, // intrinsic object type
|
||||
/* @internal */
|
||||
JsxAttributes = 1 << 25, // Jsx attributes type
|
||||
JsxAttributes = 1 << 26, // Jsx attributes type
|
||||
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
@@ -3186,12 +3188,13 @@ namespace ts {
|
||||
EnumLike = Enum | EnumLiteral,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = Object | Union | Intersection,
|
||||
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
|
||||
TypeVariable = TypeParameter | IndexedAccess,
|
||||
TypeVariable = TypeParameter | IndexedAccess | Readonly,
|
||||
StructuredOrTypeVariable = StructuredType | TypeVariable | Index,
|
||||
HasReadonlyForm = Object | Union | Intersection | TypeParameter | IndexedAccess,
|
||||
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
|
||||
Narrowable = Any | StructuredOrTypeVariable | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
|
||||
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
|
||||
@@ -3210,6 +3213,7 @@ namespace ts {
|
||||
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
|
||||
aliasSymbol?: Symbol; // Alias associated with type
|
||||
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
|
||||
readonlyType?: Type; // Readonly instance of type
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3245,9 +3249,10 @@ namespace ts {
|
||||
Tuple = 1 << 3, // Synthesized generic tuple type
|
||||
Anonymous = 1 << 4, // Anonymous
|
||||
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
|
||||
Readonly = 1 << 6, // Readonly
|
||||
Instantiated = 1 << 7, // Instantiated anonymous or mapped type
|
||||
ObjectLiteral = 1 << 8, // Originates in an object literal
|
||||
EvolvingArray = 1 << 9, // Evolving array type
|
||||
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
|
||||
ClassOrInterface = Class | Interface
|
||||
}
|
||||
@@ -3341,6 +3346,16 @@ namespace ts {
|
||||
mapper?: TypeMapper; // Instantiation mapper
|
||||
}
|
||||
|
||||
// Readonly object type (ObjectFlags.Readonly)
|
||||
export interface ReadonlyObjectType extends ObjectType {
|
||||
type: ObjectType;
|
||||
}
|
||||
|
||||
// Readonly type variable (TypeFlags.Readonly)
|
||||
export interface ReadonlyTypeVariable extends Type {
|
||||
type: TypeVariable;
|
||||
}
|
||||
|
||||
export interface EvolvingArrayType extends ObjectType {
|
||||
elementType: Type; // Element expressions of evolving array type
|
||||
finalArrayType?: Type; // Final array type of evolving array type
|
||||
|
||||
Reference in New Issue
Block a user