Skip to content

Commit 2a2962a

Browse files
authored
Properties on intersections should be readonly only if all declarations are (#45263)
Fixes #45122
1 parent 30103de commit 2a2962a

12 files changed

+183
-19
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11800,7 +11800,7 @@ namespace ts {
1180011800
// Flags we want to propagate to the result if they exist in all source symbols
1180111801
let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
1180211802
let syntheticFlag = CheckFlags.SyntheticMethod;
11803-
let checkFlags = 0;
11803+
let checkFlags = isUnion ? 0 : CheckFlags.Readonly;
1180411804
let mergedInstantiations = false;
1180511805
for (const current of containingType.types) {
1180611806
const type = getApparentType(current);
@@ -11839,8 +11839,13 @@ namespace ts {
1183911839
}
1184011840
}
1184111841
}
11842-
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
11843-
(!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
11842+
if (isUnion && isReadonlySymbol(prop)) {
11843+
checkFlags |= CheckFlags.Readonly;
11844+
}
11845+
else if (!isUnion && !isReadonlySymbol(prop)) {
11846+
checkFlags &= ~CheckFlags.Readonly;
11847+
}
11848+
checkFlags |= (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
1184411849
(modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) |
1184511850
(modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) |
1184611851
(modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0);

tests/baselines/reference/intersectionTypeReadonly.errors.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(17,6): error TS2540: Cannot assign to 'value' because it is a read-only property.
22
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(19,11): error TS2540: Cannot assign to 'value' because it is a read-only property.
3-
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(21,9): error TS2540: Cannot assign to 'value' because it is a read-only property.
43
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(23,15): error TS2540: Cannot assign to 'value' because it is a read-only property.
54
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(25,15): error TS2540: Cannot assign to 'value' because it is a read-only property.
65

76

8-
==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (5 errors) ====
7+
==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (4 errors) ====
98
interface Base {
109
readonly value: number;
1110
}
@@ -30,9 +29,7 @@ tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(25,15): e
3029
~~~~~
3130
!!! error TS2540: Cannot assign to 'value' because it is a read-only property.
3231
let mutable: Base & Mutable;
33-
mutable.value = 12; // error, lhs can't be a readonly property
34-
~~~~~
35-
!!! error TS2540: Cannot assign to 'value' because it is a read-only property.
32+
mutable.value = 12;
3633
let differentType: Base & DifferentType;
3734
differentType.value = 12; // error, lhs can't be a readonly property
3835
~~~~~

tests/baselines/reference/intersectionTypeReadonly.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ base.value = 12 // error, lhs can't be a readonly property
1919
let identical: Base & Identical;
2020
identical.value = 12; // error, lhs can't be a readonly property
2121
let mutable: Base & Mutable;
22-
mutable.value = 12; // error, lhs can't be a readonly property
22+
mutable.value = 12;
2323
let differentType: Base & DifferentType;
2424
differentType.value = 12; // error, lhs can't be a readonly property
2525
let differentName: Base & DifferentName;
@@ -32,7 +32,7 @@ base.value = 12; // error, lhs can't be a readonly property
3232
var identical;
3333
identical.value = 12; // error, lhs can't be a readonly property
3434
var mutable;
35-
mutable.value = 12; // error, lhs can't be a readonly property
35+
mutable.value = 12;
3636
var differentType;
3737
differentType.value = 12; // error, lhs can't be a readonly property
3838
var differentName;

tests/baselines/reference/intersectionTypeReadonly.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ let mutable: Base & Mutable;
5353
>Base : Symbol(Base, Decl(intersectionTypeReadonly.ts, 0, 0))
5454
>Mutable : Symbol(Mutable, Decl(intersectionTypeReadonly.ts, 5, 1))
5555

56-
mutable.value = 12; // error, lhs can't be a readonly property
56+
mutable.value = 12;
5757
>mutable.value : Symbol(value, Decl(intersectionTypeReadonly.ts, 0, 16), Decl(intersectionTypeReadonly.ts, 6, 19))
5858
>mutable : Symbol(mutable, Decl(intersectionTypeReadonly.ts, 19, 3))
5959
>value : Symbol(value, Decl(intersectionTypeReadonly.ts, 0, 16), Decl(intersectionTypeReadonly.ts, 6, 19))

tests/baselines/reference/intersectionTypeReadonly.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ identical.value = 12; // error, lhs can't be a readonly property
4242
let mutable: Base & Mutable;
4343
>mutable : Base & Mutable
4444

45-
mutable.value = 12; // error, lhs can't be a readonly property
45+
mutable.value = 12;
4646
>mutable.value = 12 : 12
47-
>mutable.value : any
47+
>mutable.value : number
4848
>mutable : Base & Mutable
49-
>value : any
49+
>value : number
5050
>12 : 12
5151

5252
let differentType: Base & DifferentType;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
tests/cases/compiler/intersectionsAndReadonlyProperties.ts(17,4): error TS2540: Cannot assign to 'a' because it is a read-only property.
2+
3+
4+
==== tests/cases/compiler/intersectionsAndReadonlyProperties.ts (1 errors) ====
5+
// readonly and non-readonly
6+
type Intersection1 = { readonly a: number } & { a: number };
7+
declare let i1: Intersection1;
8+
9+
i1.a = 2;
10+
11+
// getter and setter
12+
type Intersection2 = { get a(): number } & { set a(v: number) };
13+
declare let i2: Intersection2;
14+
15+
i2.a = 2;
16+
17+
// assignment to an all read-only property should still be disallowed
18+
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
19+
declare let ia: IntersectionAllReadonly;
20+
21+
ia.a = 2; // Error
22+
~
23+
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
24+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [intersectionsAndReadonlyProperties.ts]
2+
// readonly and non-readonly
3+
type Intersection1 = { readonly a: number } & { a: number };
4+
declare let i1: Intersection1;
5+
6+
i1.a = 2;
7+
8+
// getter and setter
9+
type Intersection2 = { get a(): number } & { set a(v: number) };
10+
declare let i2: Intersection2;
11+
12+
i2.a = 2;
13+
14+
// assignment to an all read-only property should still be disallowed
15+
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
16+
declare let ia: IntersectionAllReadonly;
17+
18+
ia.a = 2; // Error
19+
20+
21+
//// [intersectionsAndReadonlyProperties.js]
22+
i1.a = 2;
23+
i2.a = 2;
24+
ia.a = 2; // Error
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/intersectionsAndReadonlyProperties.ts ===
2+
// readonly and non-readonly
3+
type Intersection1 = { readonly a: number } & { a: number };
4+
>Intersection1 : Symbol(Intersection1, Decl(intersectionsAndReadonlyProperties.ts, 0, 0))
5+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22))
6+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 47))
7+
8+
declare let i1: Intersection1;
9+
>i1 : Symbol(i1, Decl(intersectionsAndReadonlyProperties.ts, 2, 11))
10+
>Intersection1 : Symbol(Intersection1, Decl(intersectionsAndReadonlyProperties.ts, 0, 0))
11+
12+
i1.a = 2;
13+
>i1.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22), Decl(intersectionsAndReadonlyProperties.ts, 1, 47))
14+
>i1 : Symbol(i1, Decl(intersectionsAndReadonlyProperties.ts, 2, 11))
15+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22), Decl(intersectionsAndReadonlyProperties.ts, 1, 47))
16+
17+
// getter and setter
18+
type Intersection2 = { get a(): number } & { set a(v: number) };
19+
>Intersection2 : Symbol(Intersection2, Decl(intersectionsAndReadonlyProperties.ts, 4, 9))
20+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22))
21+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 44))
22+
>v : Symbol(v, Decl(intersectionsAndReadonlyProperties.ts, 7, 51))
23+
24+
declare let i2: Intersection2;
25+
>i2 : Symbol(i2, Decl(intersectionsAndReadonlyProperties.ts, 8, 11))
26+
>Intersection2 : Symbol(Intersection2, Decl(intersectionsAndReadonlyProperties.ts, 4, 9))
27+
28+
i2.a = 2;
29+
>i2.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22), Decl(intersectionsAndReadonlyProperties.ts, 7, 44))
30+
>i2 : Symbol(i2, Decl(intersectionsAndReadonlyProperties.ts, 8, 11))
31+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22), Decl(intersectionsAndReadonlyProperties.ts, 7, 44))
32+
33+
// assignment to an all read-only property should still be disallowed
34+
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
35+
>IntersectionAllReadonly : Symbol(IntersectionAllReadonly, Decl(intersectionsAndReadonlyProperties.ts, 10, 9))
36+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32))
37+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 57))
38+
39+
declare let ia: IntersectionAllReadonly;
40+
>ia : Symbol(ia, Decl(intersectionsAndReadonlyProperties.ts, 14, 11))
41+
>IntersectionAllReadonly : Symbol(IntersectionAllReadonly, Decl(intersectionsAndReadonlyProperties.ts, 10, 9))
42+
43+
ia.a = 2; // Error
44+
>ia.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32), Decl(intersectionsAndReadonlyProperties.ts, 13, 57))
45+
>ia : Symbol(ia, Decl(intersectionsAndReadonlyProperties.ts, 14, 11))
46+
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32), Decl(intersectionsAndReadonlyProperties.ts, 13, 57))
47+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
=== tests/cases/compiler/intersectionsAndReadonlyProperties.ts ===
2+
// readonly and non-readonly
3+
type Intersection1 = { readonly a: number } & { a: number };
4+
>Intersection1 : Intersection1
5+
>a : number
6+
>a : number
7+
8+
declare let i1: Intersection1;
9+
>i1 : Intersection1
10+
11+
i1.a = 2;
12+
>i1.a = 2 : 2
13+
>i1.a : number
14+
>i1 : Intersection1
15+
>a : number
16+
>2 : 2
17+
18+
// getter and setter
19+
type Intersection2 = { get a(): number } & { set a(v: number) };
20+
>Intersection2 : Intersection2
21+
>a : number
22+
>a : number
23+
>v : number
24+
25+
declare let i2: Intersection2;
26+
>i2 : Intersection2
27+
28+
i2.a = 2;
29+
>i2.a = 2 : 2
30+
>i2.a : number
31+
>i2 : Intersection2
32+
>a : number
33+
>2 : 2
34+
35+
// assignment to an all read-only property should still be disallowed
36+
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
37+
>IntersectionAllReadonly : IntersectionAllReadonly
38+
>a : number
39+
>a : number
40+
41+
declare let ia: IntersectionAllReadonly;
42+
>ia : IntersectionAllReadonly
43+
44+
ia.a = 2; // Error
45+
>ia.a = 2 : 2
46+
>ia.a : any
47+
>ia : IntersectionAllReadonly
48+
>a : any
49+
>2 : 2
50+

tests/baselines/reference/mappedTypeRecursiveInference.types

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// readonly and non-readonly
2+
type Intersection1 = { readonly a: number } & { a: number };
3+
declare let i1: Intersection1;
4+
5+
i1.a = 2;
6+
7+
// getter and setter
8+
type Intersection2 = { get a(): number } & { set a(v: number) };
9+
declare let i2: Intersection2;
10+
11+
i2.a = 2;
12+
13+
// assignment to an all read-only property should still be disallowed
14+
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
15+
declare let ia: IntersectionAllReadonly;
16+
17+
ia.a = 2; // Error

tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ base.value = 12 // error, lhs can't be a readonly property
1818
let identical: Base & Identical;
1919
identical.value = 12; // error, lhs can't be a readonly property
2020
let mutable: Base & Mutable;
21-
mutable.value = 12; // error, lhs can't be a readonly property
21+
mutable.value = 12;
2222
let differentType: Base & DifferentType;
2323
differentType.value = 12; // error, lhs can't be a readonly property
2424
let differentName: Base & DifferentName;

0 commit comments

Comments
 (0)