From 20378e89c09083c63a31fe8f599c3ae3f9b6e019 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 19 Jul 2017 08:23:46 -1000 Subject: [PATCH] Remove special meanings of ReadonlyArray in compiler --- src/compiler/checker.ts | 68 +++++++++++++++++++++++++---------------- src/compiler/types.ts | 1 - 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c928fdaff4f..2bd24d25f30 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -294,7 +294,6 @@ namespace ts { let globalObjectType: ObjectType; let globalFunctionType: ObjectType; let globalArrayType: GenericType; - let globalReadonlyArrayType: GenericType; let globalStringType: ObjectType; let globalNumberType: ObjectType; let globalBooleanType: ObjectType; @@ -3259,7 +3258,7 @@ namespace ts { } writer.writeKeyword(type.flags & TypeFlags.Index ? "keyof" : "readonly"); writeSpace(writer); - writeType((type).type, TypeFormatFlags.InElementType); + writeType((type).type, TypeFormatFlags.InElementType); if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.CloseParenToken); } @@ -4682,7 +4681,7 @@ namespace ts { if (!popTypeResolution()) { type = reportCircularityError(symbol); } - links.type = getCheckFlags(symbol) & CheckFlags.ReadonlyType ? getReadonlyType(type) : type; + links.type = type; } } return links.type; @@ -5673,7 +5672,7 @@ namespace ts { } function createReadonlyIndexInfo(indexInfo: IndexInfo): IndexInfo { - return indexInfo && !indexInfo.isReadonly ? createIndexInfo(indexInfo.type, true, indexInfo.declaration) : indexInfo; + return indexInfo && !indexInfo.isReadonly ? createIndexInfo(indexInfo.type, /*readonly*/ true, indexInfo.declaration) : indexInfo; } function hasReadonlyThisParameter(sig: SignatureDeclaration) { @@ -5705,7 +5704,7 @@ namespace ts { const members = createMap() as SymbolTable; for (const symbol of getPropertiesOfObjectType(target)) { if (!(symbol.flags & SymbolFlags.Method) || isReadonlyThisMethod(symbol)) { - members.set(symbol.name, instantiateSymbol(symbol, identityMapper, /*readonly*/ true)); + members.set(symbol.name, isReadonlySymbol(symbol) ? symbol : instantiateSymbol(symbol, identityMapper, /*readonly*/ true)); } } const callSignatures = getSignaturesOfType(target, SignatureKind.Call); @@ -7555,7 +7554,7 @@ namespace ts { } function getReadonlyType(type: Type): Type { - if (!(type.flags & TypeFlags.HasReadonlyForm)) { + if (!(type.flags & TypeFlags.HasReadonlyForm) || getObjectFlags(type) & ObjectFlags.Readonly) { return type; } if (!type.readonlyType) { @@ -8175,7 +8174,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 | (readonly || checkFlags & CheckFlags.ReadonlyType ? CheckFlags.Readonly | CheckFlags.ReadonlyType : 0); + result.checkFlags = CheckFlags.Instantiated | (readonly ? CheckFlags.Readonly : 0); result.declarations = symbol.declarations; result.parent = symbol.parent; result.target = symbol; @@ -8333,11 +8332,13 @@ namespace ts { } function instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type { - if (type.flags & TypeFlags.TypeParameter) { + const typeFlags = type.flags; + if (typeFlags & TypeFlags.TypeParameter) { return mapper(type); } - if (type.flags & TypeFlags.Object) { - if ((type).objectFlags & ObjectFlags.Anonymous) { + if (typeFlags & TypeFlags.Object) { + const objectFlags = (type).objectFlags; + if (objectFlags & ObjectFlags.Anonymous) { // If the anonymous type originates in a declaration of a function, method, class, or // interface, in an object type literal, or in an object literal expression, we may need // to instantiate the type because it might reference a type parameter. We skip instantiation @@ -8346,26 +8347,32 @@ namespace ts { // instantiation. return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && - ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? + (objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? instantiateCached(type, mapper, instantiateAnonymousType) : type; } - if ((type).objectFlags & ObjectFlags.Mapped) { + if (objectFlags & ObjectFlags.Mapped) { return instantiateCached(type, mapper, instantiateMappedType); } - if ((type).objectFlags & ObjectFlags.Reference) { + if (objectFlags & ObjectFlags.Reference) { return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper)); } + if (objectFlags & ObjectFlags.Readonly) { + return getReadonlyType(instantiateType((type).type, mapper)); + } } - if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { + if (typeFlags & TypeFlags.Union && !(typeFlags & TypeFlags.Primitive)) { return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } - if (type.flags & TypeFlags.Intersection) { + if (typeFlags & TypeFlags.Intersection) { return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } - if (type.flags & TypeFlags.Index) { + if (typeFlags & TypeFlags.Readonly) { + return getReadonlyType(instantiateType((type).type, mapper)); + } + if (typeFlags & TypeFlags.Index) { return getIndexType(instantiateType((type).type, mapper)); } - if (type.flags & TypeFlags.IndexedAccess) { + if (typeFlags & TypeFlags.IndexedAccess) { return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); } return type; @@ -9265,10 +9272,11 @@ namespace ts { } } } - else if (target.flags & TypeFlags.Readonly) { - // A type S or readonly S is related to a readonly T if S is related to T. - const nonReadonlySource = source.flags & TypeFlags.Readonly ? (source).type : source; - if (result = isRelatedTo(nonReadonlySource, (target).type, reportErrors)) { + else if (target.flags & TypeFlags.Readonly || getObjectFlags(target) & ObjectFlags.Readonly) { + // Given types S and T, S and readonly S are related to readonly T if S is related to T. + const nonReadonlySource = source.flags & TypeFlags.Readonly || getObjectFlags(source) & ObjectFlags.Readonly ? + (source).type : source; + if (result = isRelatedTo(nonReadonlySource, (target).type, reportErrors)) { return result; } } @@ -9998,9 +10006,10 @@ namespace ts { } function isArrayLikeType(type: Type): boolean { - // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, - // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return getObjectFlags(type) & ObjectFlags.Reference && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType) || + // A type is array-like if it is an Array, a readonly Array, or if it is not undefined + // or null and it is assignable to readonly Array. + return isArrayType(type) || + getObjectFlags(type) & ObjectFlags.Readonly && isArrayType((type).type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } @@ -10353,6 +10362,7 @@ namespace ts { return !!(type.flags & TypeFlags.TypeVariable || objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeVariables) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || + objectFlags & ObjectFlags.Readonly && couldContainTypeVariables((type).type) || objectFlags & ObjectFlags.Mapped || type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(type)); } @@ -10468,6 +10478,13 @@ namespace ts { target = removeTypesFromUnionOrIntersection(target, matchingTypes); } } + if (target.flags & TypeFlags.Readonly || getObjectFlags(target) & ObjectFlags.Readonly) { + target = (target).type; + if (source.flags & TypeFlags.Readonly || getObjectFlags(source) & ObjectFlags.Readonly) { + source = (source).type; + } + } + if (target.flags & TypeFlags.TypeVariable) { // If target is a type parameter, make an inference, unless the source type contains // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions). @@ -23573,8 +23590,7 @@ namespace ts { anyArrayType = createArrayType(anyType); autoArrayType = createArrayType(autoType); - globalReadonlyArrayType = getGlobalTypeOrUndefined("ReadonlyArray" as __String, /*arity*/ 1); - anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType; + anyReadonlyArrayType = getReadonlyType(anyArrayType); globalThisType = getGlobalTypeOrUndefined("ThisType" as __String, /*arity*/ 1); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a042f08f22c..ca57c193fcd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3024,7 +3024,6 @@ 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 }