diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92e3b10842b..a6de26cc57c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3612,7 +3612,8 @@ namespace ts { createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) : createOptionalTypeNode(tupleConstituentNodes[i]); } - return createTupleTypeNode(tupleConstituentNodes); + const tupleTypeNode = createTupleTypeNode(tupleConstituentNodes); + return (type.target).readonly ? createTypeReferenceNode("Readonly", [tupleTypeNode]) : tupleTypeNode; } } if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) { @@ -5923,7 +5924,7 @@ namespace ts { function getBaseTypes(type: InterfaceType): BaseType[] { if (!type.resolvedBaseTypes) { if (type.objectFlags & ObjectFlags.Tuple) { - type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray))]; + type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray), (type).readonly)]; } else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { if (type.symbol.flags & SymbolFlags.Class) { @@ -7630,7 +7631,7 @@ namespace ts { const typeVariable = getHomomorphicTypeVariable(type); if (typeVariable) { const constraint = getConstraintOfTypeParameter(typeVariable); - if (constraint && (isArrayType(constraint) || isReadonlyArrayType(constraint) || isTupleType(constraint))) { + if (constraint && (isArrayType(constraint) || isTupleType(constraint))) { const mapper = makeUnaryTypeMapper(typeVariable, constraint); return instantiateType(type, combineTypeMappers(mapper, type.mapper)); } @@ -9022,12 +9023,8 @@ namespace ts { return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]); } - function createArrayType(elementType: Type): ObjectType { - return createTypeFromGenericGlobalType(globalArrayType, [elementType]); - } - - function createReadonlyArrayType(elementType: Type): ObjectType { - return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]); + function createArrayType(elementType: Type, readonly?: boolean): ObjectType { + return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]); } function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type { @@ -9045,7 +9042,7 @@ namespace ts { // // Note that the generic type created by this function has no symbol associated with it. The same // is true for each of the synthesized type parameters. - function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames: __String[] | undefined): TupleType { + function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType { let typeParameters: TypeParameter[] | undefined; const properties: Symbol[] = []; const maxLength = hasRestElement ? arity - 1 : arity; @@ -9054,7 +9051,8 @@ namespace ts { for (let i = 0; i < arity; i++) { const typeParameter = typeParameters[i] = createTypeParameter(); if (i < maxLength) { - const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String); + const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), + "" + i as __String, readonly ? CheckFlags.Readonly : 0); property.type = typeParameter; properties.push(property); } @@ -9083,25 +9081,26 @@ namespace ts { type.declaredNumberIndexInfo = undefined; type.minLength = minLength; type.hasRestElement = hasRestElement; + type.readonly = readonly; type.associatedNames = associatedNames; return type; } - function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames?: __String[]): GenericType { - const key = arity + (hasRestElement ? "+" : ",") + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : ""); + function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType { + const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : ""); let type = tupleTypes.get(key); if (!type) { - tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, associatedNames)); + tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames)); } return type; } - function createTupleType(elementTypes: ReadonlyArray, minLength = elementTypes.length, hasRestElement = false, associatedNames?: __String[]) { + function createTupleType(elementTypes: ReadonlyArray, minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) { const arity = elementTypes.length; if (arity === 1 && hasRestElement) { return createArrayType(elementTypes[0]); } - const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, associatedNames); + const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames); return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType; } @@ -9130,6 +9129,7 @@ namespace ts { (type.typeArguments || emptyArray).slice(index), Math.max(0, tuple.minLength - index), tuple.hasRestElement, + tuple.readonly, tuple.associatedNames && tuple.associatedNames.slice(index), ); } @@ -10694,7 +10694,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.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter)); + const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Readonly | CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter)); result.declarations = symbol.declarations; result.parent = symbol.parent; result.target = symbol; @@ -10825,11 +10825,11 @@ namespace ts { return errorType; } type.instantiating = true; + const modifiers = getMappedTypeModifiers(type); const result = mapType(mappedTypeVariable, t => { if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType) { const replacementMapper = createReplacementMapper(typeVariable, t, mapper); - return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) : - isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) : + return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper), getModifiedReadonlyState(isReadonlyArrayType(t), modifiers)) : isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) : instantiateAnonymousType(type, replacementMapper); } @@ -10842,6 +10842,10 @@ namespace ts { return instantiateAnonymousType(type, mapper); } + function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) { + return modifiers & MappedTypeModifiers.IncludeReadonly ? true : modifiers & MappedTypeModifiers.ExcludeReadonly ? false : state; + } + function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) { const minLength = tupleType.target.minLength; const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) => @@ -10850,7 +10854,8 @@ namespace ts { const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) : minLength; - return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames); + const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers); + return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames); } function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) { @@ -12608,7 +12613,7 @@ namespace ts { errorInfo = saveErrorInfo; } } - else if (isTupleType(source) && (isArrayType(target) || isReadonlyArrayType(target)) || isArrayType(source) && isReadonlyArrayType(target)) { + else if (isTupleType(source) && isArrayType(target) || isArrayType(source) && isReadonlyArrayType(target)) { return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors); } // Even if relationship doesn't hold for unions, intersections, or generic type references, @@ -13443,7 +13448,7 @@ namespace ts { } function isArrayType(type: Type): boolean { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && (type).target === globalArrayType; + return !!(getObjectFlags(type) & ObjectFlags.Reference) && ((type).target === globalArrayType || (type).target === globalReadonlyArrayType); } function isReadonlyArrayType(type: Type): boolean { @@ -13457,8 +13462,7 @@ 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) || - !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); + return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } function isEmptyArrayLiteralType(type: Type): boolean { @@ -14063,16 +14067,13 @@ namespace ts { // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been // applied to the element type(s). if (isArrayType(source)) { - return createArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint)); - } - if (isReadonlyArrayType(source)) { - return createReadonlyArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint)); + return createArrayType(inferReverseMappedType((source).typeArguments![0], target, constraint), isReadonlyArrayType(source)); } if (isTupleType(source)) { const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target, constraint)); const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ? getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength; - return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames); + return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames); } // For all other object types we infer a new object type where the reverse mapping has been // applied to the type of each property. @@ -17948,7 +17949,9 @@ namespace ts { return createTupleType(elementTypes, minLength, hasRestElement); } } - return getArrayLiteralType(elementTypes, UnionReduction.Subtype); + return createArrayType(elementTypes.length ? + getUnionType(elementTypes, UnionReduction.Subtype) : + strictNullChecks ? implicitNeverType : undefinedWideningType); } function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length) { @@ -17977,12 +17980,6 @@ namespace ts { } } - function getArrayLiteralType(elementTypes: Type[], unionReduction = UnionReduction.Literal) { - return createArrayType(elementTypes.length ? - getUnionType(elementTypes, unionReduction) : - strictNullChecks ? implicitNeverType : undefinedWideningType); - } - function isNumericName(name: DeclarationName): boolean { switch (name.kind) { case SyntaxKind.ComputedPropertyName: @@ -21276,7 +21273,7 @@ namespace ts { } const minArgumentCount = getMinArgumentCount(source); const minLength = minArgumentCount < start ? 0 : minArgumentCount - start; - return createTupleType(types, minLength, !!restType, names); + return createTupleType(types, minLength, !!restType, /*readonly*/ false, names); } function getParameterCount(signature: Signature) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e46a3d70403..c7d71cf50f9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4057,6 +4057,7 @@ namespace ts { export interface TupleType extends GenericType { minLength: number; hasRestElement: boolean; + readonly: boolean; associatedNames?: __String[]; }