Skip to content

Unions and intersections of readonly properties are now also readonly #9167

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
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11975,6 +11975,10 @@ namespace ts {
// Variables declared with 'const'
// Get accessors without matching set accessors
// Enum members
// Unions and intersections of the above
if (symbol.flags & SymbolFlags.SyntheticProperty) {
return forEach(symbol.declarations, decl => isReadonlySymbol(getSymbolOfNode(decl)));
}
Copy link
Member

@ahejlsberg ahejlsberg Jun 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be more efficient. When a symbol has multiple declarations (such as an overloaded method) we'll end up doing the same check multiple times even though the outcome is always false. Ideally we'd have a SymbolFlags.Readonly that we compute in the same way as SymbolFlags.Optional, but we're out of flag bits. Perhaps it would be better to compute the readonly state in createUnionOrIntersectionProperty (by calling isReadonlySymbol for each underlying symbol) and then record the result in an isReadonly property on the symbol itself.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a good idea. Done.

return symbol.flags & SymbolFlags.Property && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Readonly) !== 0 ||
symbol.flags & SymbolFlags.Variable && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 ||
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) ||
Expand Down
44 changes: 44 additions & 0 deletions tests/baselines/reference/intersectionTypeReadonly.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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.
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.
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.
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.
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.


==== tests/cases/conformance/types/intersection/intersectionTypeReadonly.ts (5 errors) ====
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
base.value = 12 // error, lhs can't be a readonly property
~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let identical: Base & Identical;
identical.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let mutable: Base & Mutable;
mutable.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let differentName: Base & DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist
~~~~~~~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.

39 changes: 39 additions & 0 deletions tests/baselines/reference/intersectionTypeReadonly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [intersectionTypeReadonly.ts]
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
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
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base & DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist


//// [intersectionTypeReadonly.js]
var base;
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
var differentType;
differentType.value = 12; // error, lhs can't be a readonly property
var differentName;
differentName.value = 12; // error, property 'value' doesn't exist
45 changes: 45 additions & 0 deletions tests/baselines/reference/unionTypeReadonly.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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.
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.
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.
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.
tests/cases/conformance/types/union/unionTypeReadonly.ts(25,15): error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.


==== tests/cases/conformance/types/union/unionTypeReadonly.ts (5 errors) ====
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
base.value = 12 // error, lhs can't be a readonly property
~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let identical: Base | Identical;
identical.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let mutable: Base | Mutable;
mutable.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let differentType: Base | DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
~~~~~~~~~~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
let differentName: Base | DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist
~~~~~
!!! error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.


40 changes: 40 additions & 0 deletions tests/baselines/reference/unionTypeReadonly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [unionTypeReadonly.ts]
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
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
let differentType: Base | DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base | DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist



//// [unionTypeReadonly.js]
var base;
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
var differentType;
differentType.value = 12; // error, lhs can't be a readonly property
var differentName;
differentName.value = 12; // error, property 'value' doesn't exist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
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
let differentType: Base & DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base & DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist
26 changes: 26 additions & 0 deletions tests/cases/conformance/types/union/unionTypeReadonly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
interface Base {
readonly value: number;
}
interface Identical {
readonly value: number;
}
interface Mutable {
value: number;
}
interface DifferentType {
readonly value: string;
}
interface DifferentName {
readonly other: number;
}
let base: Base;
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
let differentType: Base | DifferentType;
differentType.value = 12; // error, lhs can't be a readonly property
let differentName: Base | DifferentName;
differentName.value = 12; // error, property 'value' doesn't exist