Skip to content

Properties on intersections should be readonly only if all declarations are #45263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11800,7 +11800,7 @@ namespace ts {
// Flags we want to propagate to the result if they exist in all source symbols
let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
let syntheticFlag = CheckFlags.SyntheticMethod;
let checkFlags = 0;
let checkFlags = isUnion ? 0 : CheckFlags.Readonly;
let mergedInstantiations = false;
for (const current of containingType.types) {
const type = getApparentType(current);
Expand Down Expand Up @@ -11839,8 +11839,13 @@ namespace ts {
}
}
}
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
(!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
if (isUnion && isReadonlySymbol(prop)) {
checkFlags |= CheckFlags.Readonly;
}
else if (!isUnion && !isReadonlySymbol(prop)) {
checkFlags &= ~CheckFlags.Readonly;
}
checkFlags |= (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
(modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) |
(modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) |
(modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0);
Expand Down
7 changes: 2 additions & 5 deletions tests/baselines/reference/intersectionTypeReadonly.errors.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(17,6): error TS2540: Cannot assign to 'value' because it is a read-only property.
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(19,11): error TS2540: Cannot assign to 'value' because it is a read-only property.
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(21,9): error TS2540: Cannot assign to 'value' because it is a read-only property.
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(23,15): error TS2540: Cannot assign to 'value' because it is a read-only property.
tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(25,15): error TS2540: Cannot assign to 'value' because it is a read-only property.


==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (5 errors) ====
==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (4 errors) ====
interface Base {
readonly value: number;
}
Expand All @@ -30,9 +29,7 @@ tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts(25,15): e
~~~~~
!!! error TS2540: Cannot assign to 'value' because it is a read-only property.
let mutable: Base & Mutable;
mutable.value = 12; // error, lhs can't be a readonly property
~~~~~
!!! error TS2540: Cannot assign to 'value' because it is a read-only property.
mutable.value = 12;
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
~~~~~
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/intersectionTypeReadonly.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ base.value = 12 // error, lhs can't be a readonly property
let identical: Base & Identical;
identical.value = 12; // error, lhs can't be a readonly property
let mutable: Base & Mutable;
mutable.value = 12; // error, lhs can't be a readonly property
mutable.value = 12;
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base & DifferentName;
Expand All @@ -32,7 +32,7 @@ base.value = 12; // error, lhs can't be a readonly property
var identical;
identical.value = 12; // error, lhs can't be a readonly property
var mutable;
mutable.value = 12; // error, lhs can't be a readonly property
mutable.value = 12;
var differentType;
differentType.value = 12; // error, lhs can't be a readonly property
var differentName;
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/intersectionTypeReadonly.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ let mutable: Base & Mutable;
>Base : Symbol(Base, Decl(intersectionTypeReadonly.ts, 0, 0))
>Mutable : Symbol(Mutable, Decl(intersectionTypeReadonly.ts, 5, 1))

mutable.value = 12; // error, lhs can't be a readonly property
mutable.value = 12;
>mutable.value : Symbol(value, Decl(intersectionTypeReadonly.ts, 0, 16), Decl(intersectionTypeReadonly.ts, 6, 19))
>mutable : Symbol(mutable, Decl(intersectionTypeReadonly.ts, 19, 3))
>value : Symbol(value, Decl(intersectionTypeReadonly.ts, 0, 16), Decl(intersectionTypeReadonly.ts, 6, 19))
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/intersectionTypeReadonly.types
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ identical.value = 12; // error, lhs can't be a readonly property
let mutable: Base & Mutable;
>mutable : Base & Mutable

mutable.value = 12; // error, lhs can't be a readonly property
mutable.value = 12;
>mutable.value = 12 : 12
>mutable.value : any
>mutable.value : number
>mutable : Base & Mutable
>value : any
>value : number
>12 : 12

let differentType: Base & DifferentType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
tests/cases/compiler/intersectionsAndReadonlyProperties.ts(17,4): error TS2540: Cannot assign to 'a' because it is a read-only property.


==== tests/cases/compiler/intersectionsAndReadonlyProperties.ts (1 errors) ====
// readonly and non-readonly
type Intersection1 = { readonly a: number } & { a: number };
declare let i1: Intersection1;

i1.a = 2;

// getter and setter
type Intersection2 = { get a(): number } & { set a(v: number) };
declare let i2: Intersection2;

i2.a = 2;

// assignment to an all read-only property should still be disallowed
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
declare let ia: IntersectionAllReadonly;

ia.a = 2; // Error
~
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.

24 changes: 24 additions & 0 deletions tests/baselines/reference/intersectionsAndReadonlyProperties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [intersectionsAndReadonlyProperties.ts]
// readonly and non-readonly
type Intersection1 = { readonly a: number } & { a: number };
declare let i1: Intersection1;

i1.a = 2;

// getter and setter
type Intersection2 = { get a(): number } & { set a(v: number) };
declare let i2: Intersection2;

i2.a = 2;

// assignment to an all read-only property should still be disallowed
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
declare let ia: IntersectionAllReadonly;

ia.a = 2; // Error


//// [intersectionsAndReadonlyProperties.js]
i1.a = 2;
i2.a = 2;
ia.a = 2; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
=== tests/cases/compiler/intersectionsAndReadonlyProperties.ts ===
// readonly and non-readonly
type Intersection1 = { readonly a: number } & { a: number };
>Intersection1 : Symbol(Intersection1, Decl(intersectionsAndReadonlyProperties.ts, 0, 0))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 47))

declare let i1: Intersection1;
>i1 : Symbol(i1, Decl(intersectionsAndReadonlyProperties.ts, 2, 11))
>Intersection1 : Symbol(Intersection1, Decl(intersectionsAndReadonlyProperties.ts, 0, 0))

i1.a = 2;
>i1.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22), Decl(intersectionsAndReadonlyProperties.ts, 1, 47))
>i1 : Symbol(i1, Decl(intersectionsAndReadonlyProperties.ts, 2, 11))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 1, 22), Decl(intersectionsAndReadonlyProperties.ts, 1, 47))

// getter and setter
type Intersection2 = { get a(): number } & { set a(v: number) };
>Intersection2 : Symbol(Intersection2, Decl(intersectionsAndReadonlyProperties.ts, 4, 9))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 44))
>v : Symbol(v, Decl(intersectionsAndReadonlyProperties.ts, 7, 51))

declare let i2: Intersection2;
>i2 : Symbol(i2, Decl(intersectionsAndReadonlyProperties.ts, 8, 11))
>Intersection2 : Symbol(Intersection2, Decl(intersectionsAndReadonlyProperties.ts, 4, 9))

i2.a = 2;
>i2.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22), Decl(intersectionsAndReadonlyProperties.ts, 7, 44))
>i2 : Symbol(i2, Decl(intersectionsAndReadonlyProperties.ts, 8, 11))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 7, 22), Decl(intersectionsAndReadonlyProperties.ts, 7, 44))

// assignment to an all read-only property should still be disallowed
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
>IntersectionAllReadonly : Symbol(IntersectionAllReadonly, Decl(intersectionsAndReadonlyProperties.ts, 10, 9))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 57))

declare let ia: IntersectionAllReadonly;
>ia : Symbol(ia, Decl(intersectionsAndReadonlyProperties.ts, 14, 11))
>IntersectionAllReadonly : Symbol(IntersectionAllReadonly, Decl(intersectionsAndReadonlyProperties.ts, 10, 9))

ia.a = 2; // Error
>ia.a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32), Decl(intersectionsAndReadonlyProperties.ts, 13, 57))
>ia : Symbol(ia, Decl(intersectionsAndReadonlyProperties.ts, 14, 11))
>a : Symbol(a, Decl(intersectionsAndReadonlyProperties.ts, 13, 32), Decl(intersectionsAndReadonlyProperties.ts, 13, 57))

50 changes: 50 additions & 0 deletions tests/baselines/reference/intersectionsAndReadonlyProperties.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
=== tests/cases/compiler/intersectionsAndReadonlyProperties.ts ===
// readonly and non-readonly
type Intersection1 = { readonly a: number } & { a: number };
>Intersection1 : Intersection1
>a : number
>a : number

declare let i1: Intersection1;
>i1 : Intersection1

i1.a = 2;
>i1.a = 2 : 2
>i1.a : number
>i1 : Intersection1
>a : number
>2 : 2

// getter and setter
type Intersection2 = { get a(): number } & { set a(v: number) };
>Intersection2 : Intersection2
>a : number
>a : number
>v : number

declare let i2: Intersection2;
>i2 : Intersection2

i2.a = 2;
>i2.a = 2 : 2
>i2.a : number
>i2 : Intersection2
>a : number
>2 : 2

// assignment to an all read-only property should still be disallowed
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
>IntersectionAllReadonly : IntersectionAllReadonly
>a : number
>a : number

declare let ia: IntersectionAllReadonly;
>ia : IntersectionAllReadonly

ia.a = 2; // Error
>ia.a = 2 : 2
>ia.a : any
>ia : IntersectionAllReadonly
>a : any
>2 : 2

8 changes: 4 additions & 4 deletions tests/baselines/reference/mappedTypeRecursiveInference.types

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions tests/cases/compiler/intersectionsAndReadonlyProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// readonly and non-readonly
type Intersection1 = { readonly a: number } & { a: number };
declare let i1: Intersection1;

i1.a = 2;

// getter and setter
type Intersection2 = { get a(): number } & { set a(v: number) };
declare let i2: Intersection2;

i2.a = 2;

// assignment to an all read-only property should still be disallowed
type IntersectionAllReadonly = { readonly a: number } & { get a(): number };
declare let ia: IntersectionAllReadonly;

ia.a = 2; // Error
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ base.value = 12 // error, lhs can't be a readonly property
let identical: Base & Identical;
identical.value = 12; // error, lhs can't be a readonly property
let mutable: Base & Mutable;
mutable.value = 12; // error, lhs can't be a readonly property
mutable.value = 12;
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base & DifferentName;
Expand Down