Skip to content

Commit fb3f823

Browse files
authored
Merge pull request #9167 from Microsoft/make-unions-and-intersections-of-readonly-properties-readonly
Unions and intersections of readonly properties are now also readonly
2 parents 0b4b372 + cc8d193 commit fb3f823

File tree

8 files changed

+228
-1
lines changed

8 files changed

+228
-1
lines changed

src/compiler/checker.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -4233,6 +4233,7 @@ namespace ts {
42334233
let props: Symbol[];
42344234
// Flags we want to propagate to the result if they exist in all source symbols
42354235
let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
4236+
let isReadonly = false;
42364237
for (const current of types) {
42374238
const type = getApparentType(current);
42384239
if (type !== unknownType) {
@@ -4245,6 +4246,9 @@ namespace ts {
42454246
else if (!contains(props, prop)) {
42464247
props.push(prop);
42474248
}
4249+
if (isReadonlySymbol(prop)) {
4250+
isReadonly = true;
4251+
}
42484252
}
42494253
else if (containingType.flags & TypeFlags.Union) {
42504254
// A union type requires the property to be present in all constituent types
@@ -4274,6 +4278,7 @@ namespace ts {
42744278
name);
42754279
result.containingType = containingType;
42764280
result.declarations = declarations;
4281+
result.isReadonly = isReadonly;
42774282
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
42784283
return result;
42794284
}
@@ -12204,7 +12209,9 @@ namespace ts {
1220412209
// Variables declared with 'const'
1220512210
// Get accessors without matching set accessors
1220612211
// Enum members
12207-
return symbol.flags & SymbolFlags.Property && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Readonly) !== 0 ||
12212+
// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
12213+
return symbol.isReadonly ||
12214+
symbol.flags & SymbolFlags.Property && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Readonly) !== 0 ||
1220812215
symbol.flags & SymbolFlags.Variable && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 ||
1220912216
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) ||
1221012217
(symbol.flags & SymbolFlags.EnumMember) !== 0;

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,7 @@ namespace ts {
21222122
members?: SymbolTable; // Class, interface or literal instance members
21232123
exports?: SymbolTable; // Module exports
21242124
globalExports?: SymbolTable; // Conditional global UMD exports
2125+
/* @internal */ isReadonly?: boolean; // readonly? (set only for intersections and unions)
21252126
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
21262127
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
21272128
/* @internal */ parent?: Symbol; // Parent symbol
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(17,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
2+
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(19,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
3+
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(21,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
4+
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(23,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
5+
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(25,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
6+
7+
8+
==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (5 errors) ====
9+
interface Base {
10+
readonly value: number;
11+
}
12+
interface Identical {
13+
readonly value: number;
14+
}
15+
interface Mutable {
16+
value: number;
17+
}
18+
interface DifferentType {
19+
readonly value: string;
20+
}
21+
interface DifferentName {
22+
readonly other: number;
23+
}
24+
let base: Base;
25+
base.value = 12 // error, lhs can't be a readonly property
26+
~~~~~~~~~~
27+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
28+
let identical: Base & Identical;
29+
identical.value = 12; // error, lhs can't be a readonly property
30+
~~~~~~~~~~~~~~~
31+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
32+
let mutable: Base & Mutable;
33+
mutable.value = 12; // error, lhs can't be a readonly property
34+
~~~~~~~~~~~~~
35+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
36+
let differentType: Base & DifferentType;
37+
differentType.value = 12; // error, lhs can't be a readonly property
38+
~~~~~~~~~~~~~~~~~~~
39+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
40+
let differentName: Base & DifferentName;
41+
differentName.value = 12; // error, property 'value' doesn't exist
42+
~~~~~~~~~~~~~~~~~~~
43+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [intersectionTypeReadonly.ts]
2+
interface Base {
3+
readonly value: number;
4+
}
5+
interface Identical {
6+
readonly value: number;
7+
}
8+
interface Mutable {
9+
value: number;
10+
}
11+
interface DifferentType {
12+
readonly value: string;
13+
}
14+
interface DifferentName {
15+
readonly other: number;
16+
}
17+
let base: Base;
18+
base.value = 12 // error, lhs can't be a readonly property
19+
let identical: Base & Identical;
20+
identical.value = 12; // error, lhs can't be a readonly property
21+
let mutable: Base & Mutable;
22+
mutable.value = 12; // error, lhs can't be a readonly property
23+
let differentType: Base & DifferentType;
24+
differentType.value = 12; // error, lhs can't be a readonly property
25+
let differentName: Base & DifferentName;
26+
differentName.value = 12; // error, property 'value' doesn't exist
27+
28+
29+
//// [intersectionTypeReadonly.js]
30+
var base;
31+
base.value = 12; // error, lhs can't be a readonly property
32+
var identical;
33+
identical.value = 12; // error, lhs can't be a readonly property
34+
var mutable;
35+
mutable.value = 12; // error, lhs can't be a readonly property
36+
var differentType;
37+
differentType.value = 12; // error, lhs can't be a readonly property
38+
var differentName;
39+
differentName.value = 12; // error, property 'value' doesn't exist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
tests/cases/conformance/types/union/unionTypeReadonly.ts(17,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
2+
tests/cases/conformance/types/union/unionTypeReadonly.ts(19,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
3+
tests/cases/conformance/types/union/unionTypeReadonly.ts(21,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
4+
tests/cases/conformance/types/union/unionTypeReadonly.ts(23,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
5+
tests/cases/conformance/types/union/unionTypeReadonly.ts(25,15): error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
6+
7+
8+
==== tests/cases/conformance/types/union/unionTypeReadonly.ts (5 errors) ====
9+
interface Base {
10+
readonly value: number;
11+
}
12+
interface Identical {
13+
readonly value: number;
14+
}
15+
interface Mutable {
16+
value: number;
17+
}
18+
interface DifferentType {
19+
readonly value: string;
20+
}
21+
interface DifferentName {
22+
readonly other: number;
23+
}
24+
let base: Base;
25+
base.value = 12 // error, lhs can't be a readonly property
26+
~~~~~~~~~~
27+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
28+
let identical: Base | Identical;
29+
identical.value = 12; // error, lhs can't be a readonly property
30+
~~~~~~~~~~~~~~~
31+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
32+
let mutable: Base | Mutable;
33+
mutable.value = 12; // error, lhs can't be a readonly property
34+
~~~~~~~~~~~~~
35+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
36+
let differentType: Base | DifferentType;
37+
differentType.value = 12; // error, lhs can't be a readonly property
38+
~~~~~~~~~~~~~~~~~~~
39+
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
40+
let differentName: Base | DifferentName;
41+
differentName.value = 12; // error, property 'value' doesn't exist
42+
~~~~~
43+
!!! error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
44+
45+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [unionTypeReadonly.ts]
2+
interface Base {
3+
readonly value: number;
4+
}
5+
interface Identical {
6+
readonly value: number;
7+
}
8+
interface Mutable {
9+
value: number;
10+
}
11+
interface DifferentType {
12+
readonly value: string;
13+
}
14+
interface DifferentName {
15+
readonly other: number;
16+
}
17+
let base: Base;
18+
base.value = 12 // error, lhs can't be a readonly property
19+
let identical: Base | Identical;
20+
identical.value = 12; // error, lhs can't be a readonly property
21+
let mutable: Base | Mutable;
22+
mutable.value = 12; // error, lhs can't be a readonly property
23+
let differentType: Base | DifferentType;
24+
differentType.value = 12; // error, lhs can't be a readonly property
25+
let differentName: Base | DifferentName;
26+
differentName.value = 12; // error, property 'value' doesn't exist
27+
28+
29+
30+
//// [unionTypeReadonly.js]
31+
var base;
32+
base.value = 12; // error, lhs can't be a readonly property
33+
var identical;
34+
identical.value = 12; // error, lhs can't be a readonly property
35+
var mutable;
36+
mutable.value = 12; // error, lhs can't be a readonly property
37+
var differentType;
38+
differentType.value = 12; // error, lhs can't be a readonly property
39+
var differentName;
40+
differentName.value = 12; // error, property 'value' doesn't exist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
interface Base {
2+
readonly value: number;
3+
}
4+
interface Identical {
5+
readonly value: number;
6+
}
7+
interface Mutable {
8+
value: number;
9+
}
10+
interface DifferentType {
11+
readonly value: string;
12+
}
13+
interface DifferentName {
14+
readonly other: number;
15+
}
16+
let base: Base;
17+
base.value = 12 // error, lhs can't be a readonly property
18+
let identical: Base & Identical;
19+
identical.value = 12; // error, lhs can't be a readonly property
20+
let mutable: Base & Mutable;
21+
mutable.value = 12; // error, lhs can't be a readonly property
22+
let differentType: Base & DifferentType;
23+
differentType.value = 12; // error, lhs can't be a readonly property
24+
let differentName: Base & DifferentName;
25+
differentName.value = 12; // error, property 'value' doesn't exist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
interface Base {
2+
readonly value: number;
3+
}
4+
interface Identical {
5+
readonly value: number;
6+
}
7+
interface Mutable {
8+
value: number;
9+
}
10+
interface DifferentType {
11+
readonly value: string;
12+
}
13+
interface DifferentName {
14+
readonly other: number;
15+
}
16+
let base: Base;
17+
base.value = 12 // error, lhs can't be a readonly property
18+
let identical: Base | Identical;
19+
identical.value = 12; // error, lhs can't be a readonly property
20+
let mutable: Base | Mutable;
21+
mutable.value = 12; // error, lhs can't be a readonly property
22+
let differentType: Base | DifferentType;
23+
differentType.value = 12; // error, lhs can't be a readonly property
24+
let differentName: Base | DifferentName;
25+
differentName.value = 12; // error, property 'value' doesn't exist
26+

0 commit comments

Comments
 (0)