Skip to content

Commit 903863a

Browse files
committed
Respect readonly mapped type modifier when mapping arrays and tuples
1 parent 52b8256 commit 903863a

File tree

2 files changed

+35
-37
lines changed

2 files changed

+35
-37
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3612,7 +3612,8 @@ namespace ts {
36123612
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
36133613
createOptionalTypeNode(tupleConstituentNodes[i]);
36143614
}
3615-
return createTupleTypeNode(tupleConstituentNodes);
3615+
const tupleTypeNode = createTupleTypeNode(tupleConstituentNodes);
3616+
return (<TupleType>type.target).readonly ? createTypeReferenceNode("Readonly", [tupleTypeNode]) : tupleTypeNode;
36163617
}
36173618
}
36183619
if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) {
@@ -5923,7 +5924,7 @@ namespace ts {
59235924
function getBaseTypes(type: InterfaceType): BaseType[] {
59245925
if (!type.resolvedBaseTypes) {
59255926
if (type.objectFlags & ObjectFlags.Tuple) {
5926-
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray))];
5927+
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray), (<TupleType>type).readonly)];
59275928
}
59285929
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
59295930
if (type.symbol.flags & SymbolFlags.Class) {
@@ -7630,7 +7631,7 @@ namespace ts {
76307631
const typeVariable = getHomomorphicTypeVariable(type);
76317632
if (typeVariable) {
76327633
const constraint = getConstraintOfTypeParameter(typeVariable);
7633-
if (constraint && (isArrayType(constraint) || isReadonlyArrayType(constraint) || isTupleType(constraint))) {
7634+
if (constraint && (isArrayType(constraint) || isTupleType(constraint))) {
76347635
const mapper = makeUnaryTypeMapper(typeVariable, constraint);
76357636
return instantiateType(type, combineTypeMappers(mapper, type.mapper));
76367637
}
@@ -9022,12 +9023,8 @@ namespace ts {
90229023
return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]);
90239024
}
90249025

9025-
function createArrayType(elementType: Type): ObjectType {
9026-
return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
9027-
}
9028-
9029-
function createReadonlyArrayType(elementType: Type): ObjectType {
9030-
return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
9026+
function createArrayType(elementType: Type, readonly?: boolean): ObjectType {
9027+
return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]);
90319028
}
90329029

90339030
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
@@ -9045,7 +9042,7 @@ namespace ts {
90459042
//
90469043
// Note that the generic type created by this function has no symbol associated with it. The same
90479044
// is true for each of the synthesized type parameters.
9048-
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames: __String[] | undefined): TupleType {
9045+
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType {
90499046
let typeParameters: TypeParameter[] | undefined;
90509047
const properties: Symbol[] = [];
90519048
const maxLength = hasRestElement ? arity - 1 : arity;
@@ -9054,7 +9051,8 @@ namespace ts {
90549051
for (let i = 0; i < arity; i++) {
90559052
const typeParameter = typeParameters[i] = createTypeParameter();
90569053
if (i < maxLength) {
9057-
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
9054+
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0),
9055+
"" + i as __String, readonly ? CheckFlags.Readonly : 0);
90589056
property.type = typeParameter;
90599057
properties.push(property);
90609058
}
@@ -9083,25 +9081,26 @@ namespace ts {
90839081
type.declaredNumberIndexInfo = undefined;
90849082
type.minLength = minLength;
90859083
type.hasRestElement = hasRestElement;
9084+
type.readonly = readonly;
90869085
type.associatedNames = associatedNames;
90879086
return type;
90889087
}
90899088

9090-
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames?: __String[]): GenericType {
9091-
const key = arity + (hasRestElement ? "+" : ",") + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
9089+
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType {
9090+
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
90929091
let type = tupleTypes.get(key);
90939092
if (!type) {
9094-
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, associatedNames));
9093+
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames));
90959094
}
90969095
return type;
90979096
}
90989097

9099-
function createTupleType(elementTypes: ReadonlyArray<Type>, minLength = elementTypes.length, hasRestElement = false, associatedNames?: __String[]) {
9098+
function createTupleType(elementTypes: ReadonlyArray<Type>, minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) {
91009099
const arity = elementTypes.length;
91019100
if (arity === 1 && hasRestElement) {
91029101
return createArrayType(elementTypes[0]);
91039102
}
9104-
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, associatedNames);
9103+
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames);
91059104
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
91069105
}
91079106

@@ -9130,6 +9129,7 @@ namespace ts {
91309129
(type.typeArguments || emptyArray).slice(index),
91319130
Math.max(0, tuple.minLength - index),
91329131
tuple.hasRestElement,
9132+
tuple.readonly,
91339133
tuple.associatedNames && tuple.associatedNames.slice(index),
91349134
);
91359135
}
@@ -10694,7 +10694,7 @@ namespace ts {
1069410694
}
1069510695
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
1069610696
// also transient so that we can just store data on it directly.
10697-
const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter));
10697+
const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Readonly | CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter));
1069810698
result.declarations = symbol.declarations;
1069910699
result.parent = symbol.parent;
1070010700
result.target = symbol;
@@ -10825,11 +10825,11 @@ namespace ts {
1082510825
return errorType;
1082610826
}
1082710827
type.instantiating = true;
10828+
const modifiers = getMappedTypeModifiers(type);
1082810829
const result = mapType(mappedTypeVariable, t => {
1082910830
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType) {
1083010831
const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
10831-
return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10832-
isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10832+
return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper), getModifiedReadonlyState(isReadonlyArrayType(t), modifiers)) :
1083310833
isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
1083410834
instantiateAnonymousType(type, replacementMapper);
1083510835
}
@@ -10842,6 +10842,10 @@ namespace ts {
1084210842
return instantiateAnonymousType(type, mapper);
1084310843
}
1084410844

10845+
function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) {
10846+
return modifiers & MappedTypeModifiers.IncludeReadonly ? true : modifiers & MappedTypeModifiers.ExcludeReadonly ? false : state;
10847+
}
10848+
1084510849
function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
1084610850
const minLength = tupleType.target.minLength;
1084710851
const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
@@ -10850,7 +10854,8 @@ namespace ts {
1085010854
const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 :
1085110855
modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
1085210856
minLength;
10853-
return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames);
10857+
const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
10858+
return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
1085410859
}
1085510860

1085610861
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
@@ -12608,7 +12613,7 @@ namespace ts {
1260812613
errorInfo = saveErrorInfo;
1260912614
}
1261012615
}
12611-
else if (isTupleType(source) && (isArrayType(target) || isReadonlyArrayType(target)) || isArrayType(source) && isReadonlyArrayType(target)) {
12616+
else if (isTupleType(source) && isArrayType(target) || isArrayType(source) && isReadonlyArrayType(target)) {
1261212617
return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors);
1261312618
}
1261412619
// Even if relationship doesn't hold for unions, intersections, or generic type references,
@@ -13443,7 +13448,7 @@ namespace ts {
1344313448
}
1344413449

1344513450
function isArrayType(type: Type): boolean {
13446-
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalArrayType;
13451+
return !!(getObjectFlags(type) & ObjectFlags.Reference) && ((<TypeReference>type).target === globalArrayType || (<TypeReference>type).target === globalReadonlyArrayType);
1344713452
}
1344813453

1344913454
function isReadonlyArrayType(type: Type): boolean {
@@ -13457,8 +13462,7 @@ namespace ts {
1345713462
function isArrayLikeType(type: Type): boolean {
1345813463
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
1345913464
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
13460-
return getObjectFlags(type) & ObjectFlags.Reference && ((<TypeReference>type).target === globalArrayType || (<TypeReference>type).target === globalReadonlyArrayType) ||
13461-
!(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
13465+
return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
1346213466
}
1346313467

1346413468
function isEmptyArrayLiteralType(type: Type): boolean {
@@ -14063,16 +14067,13 @@ namespace ts {
1406314067
// For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
1406414068
// applied to the element type(s).
1406514069
if (isArrayType(source)) {
14066-
return createArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target, constraint));
14067-
}
14068-
if (isReadonlyArrayType(source)) {
14069-
return createReadonlyArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target, constraint));
14070+
return createArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target, constraint), isReadonlyArrayType(source));
1407014071
}
1407114072
if (isTupleType(source)) {
1407214073
const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target, constraint));
1407314074
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
1407414075
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
14075-
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames);
14076+
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames);
1407614077
}
1407714078
// For all other object types we infer a new object type where the reverse mapping has been
1407814079
// applied to the type of each property.
@@ -17948,7 +17949,9 @@ namespace ts {
1794817949
return createTupleType(elementTypes, minLength, hasRestElement);
1794917950
}
1795017951
}
17951-
return getArrayLiteralType(elementTypes, UnionReduction.Subtype);
17952+
return createArrayType(elementTypes.length ?
17953+
getUnionType(elementTypes, UnionReduction.Subtype) :
17954+
strictNullChecks ? implicitNeverType : undefinedWideningType);
1795217955
}
1795317956

1795417957
function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length) {
@@ -17977,12 +17980,6 @@ namespace ts {
1797717980
}
1797817981
}
1797917982

17980-
function getArrayLiteralType(elementTypes: Type[], unionReduction = UnionReduction.Literal) {
17981-
return createArrayType(elementTypes.length ?
17982-
getUnionType(elementTypes, unionReduction) :
17983-
strictNullChecks ? implicitNeverType : undefinedWideningType);
17984-
}
17985-
1798617983
function isNumericName(name: DeclarationName): boolean {
1798717984
switch (name.kind) {
1798817985
case SyntaxKind.ComputedPropertyName:
@@ -21276,7 +21273,7 @@ namespace ts {
2127621273
}
2127721274
const minArgumentCount = getMinArgumentCount(source);
2127821275
const minLength = minArgumentCount < start ? 0 : minArgumentCount - start;
21279-
return createTupleType(types, minLength, !!restType, names);
21276+
return createTupleType(types, minLength, !!restType, /*readonly*/ false, names);
2128021277
}
2128121278

2128221279
function getParameterCount(signature: Signature) {

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4057,6 +4057,7 @@ namespace ts {
40574057
export interface TupleType extends GenericType {
40584058
minLength: number;
40594059
hasRestElement: boolean;
4060+
readonly: boolean;
40604061
associatedNames?: __String[];
40614062
}
40624063

0 commit comments

Comments
 (0)