GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Readonly properties and index signatures by ahejlsberg · Pull Request #6532 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jan 19, 2016

Copy link
Copy Markdown
Member

This PR adds support for readonly properties and index signatures as suggested in #12. Changes include:

  • A new readonly modifier that can applied to property and index signature declarations.
  • A property or index signature declared with the readonly modifier is considered read-only.
  • Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.
  • Assignments to read-only index signatures are always disallowed.

Entities are implicitly read-only in several situations:

  • A property declared with a get accessor and no set accessor is considered read-only.
  • In the type of an enum object, enum members are considered read-only properties.
  • In the type of a module object, exported const variables are considered read-only properties.
  • An entity declared in an import statement is considered read-only.
  • An entity accessed through an ES2015 namespace import is considered read-only (e.g. foo.x is read-only when foo is declared as import * as foo from "foo").

Some examples:

interface Point {
    readonly x: number;
    readonly y: number;
}

var p1: Point = { x: 10, y: 20 };
p1.x = 5;  // Error, p1.x is read-only

var p2 = { x: 1, y: 1 };
var p3: Point = p2;  // Ok, read-only alias for p2
p3.x = 5;  // Error, p3.x is read-only
p2.x = 5;  // Ok, but also changes p3.x because of aliasing
class Foo {
    readonly a = 1;
    readonly b: string;
    constructor() {
        this.b = "hello";  // Assignment permitted in constructor
    }
}
// Read-only view of Array<T> with all mutating methods removed
interface ReadonlyArray<T> {
    readonly [x: number]: T;
    readonly length: number;
    toString(): string;
    toLocaleString(): string;
    concat<U extends ReadonlyArray<T>>(...items: U[]): T[];
    concat(...items: T[]): T[];
    join(separator?: string): string;
    slice(start?: number, end?: number): T[];
    indexOf(searchElement: T, fromIndex?: number): number;
    lastIndexOf(searchElement: T, fromIndex?: number): number;
    every(callbackfn: (value: T, index: number) => boolean, thisArg?: any): boolean;
    some(callbackfn: (value: T, index: number) => boolean, thisArg?: any): boolean;
    forEach(callbackfn: (value: T, index: number) => void, thisArg?: any): void;
    map<U>(callbackfn: (value: T, index: number) => U, thisArg?: any): U[];
    filter(callbackfn: (value: T, index: number) => boolean, thisArg?: any): T[];
    reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T, initialValue?: T): T;
    reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U;
    reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T, initialValue?: T): T;
    reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U;
}

let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5;      // Error, elements are read-only
b.push(5);     // Error, no push method (because it mutates array)
b.length = 3;  // Error, length is read-only
a = b;         // Error, mutating methods are missing

Note that the readonly modifier does not affect subtype and assignability relationships of the containing type for the reasons described here. Also note that this PR specifically doesn't attempt to formalize immutable objects. However, with the readonly modifier it becomes a lot more convenient to implement such objects.

The PR still needs tests, but I thought I'd get it up there for folks to take a look at.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sergey-shandar, trunda, cluda, garkin, vvakame, RyanQuackenbush, agu-z, NoelAbrahams, soyeyalon, gulshan, and 18 more reacted with thumbs up emoji sergey-shandar, mquandalle, SlurpTheo, sccolbert, monagames, michaelmesser, guncha, ladas-larry, pwnall, Meowtimer, and 4 more reacted with hooray emoji sergey-shandar, mquandalle, cluda, ulrichb, michalstocki, soyeyalon, pgatilov, ryanmillerdev, mohsen1, jonaskello, and 4 more reacted with heart emoji
All reactions
  • 👍 28 reactions
  • 🎉 14 reactions
  • ❤️ 14 reactions
Anders Hejlsberg (ahejlsberg) added 13 commits January 13, 2016 18:34
@ahejlsberg
Allow modifies on type members + introduce "readonly" modifier
b46efc9
@ahejlsberg
Accepting new baselines
07b7008
@ahejlsberg
Check readonly in assignments and type relations
d504357
@ahejlsberg
Accepting new baselines
8319f2e
@ahejlsberg
Removing unused function
07763ed
@ahejlsberg
Refactor to introduce IndexInfo type
5b233e4
@ahejlsberg
Support readonly indexers + include readonly modifier in typeToString
b8cd0e5
@ahejlsberg
Fixing fourslash test
17b5899
@ahejlsberg
Accepting new baselines
13f763d
@ahejlsberg
Allow assignments to readonly properties in constructors
2a20e91
@ahejlsberg
Allow 'readonly' only on property and index signature declarations
01c9686
@ahejlsberg
Updating error messages
d3be38e
@ahejlsberg
Accepting new baselines
723475b
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label Jan 19, 2016
@ahejlsberg
Merge branch 'master' into readonlyMembers
a499607
Conflicts:
	src/compiler/parser.ts
	src/compiler/types.ts
@JsonFreeman

Jason Freeman (JsonFreeman) commented Jan 19, 2016

Copy link
Copy Markdown
Contributor

Yay!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

JsonFreeman
JsonFreeman reviewed Jan 19, 2016
View reviewed changes
Comment thread src/compiler/checker.ts Outdated

@JsonFreeman Jason Freeman (JsonFreeman) Jan 19, 2016

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Why is this indexer not readonly?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Jan 19, 2016

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I believe because we don't want to break this code:

enum SyntaxKind { Node, OtherNode }
function dbgPrint(value: number, yourEnum: { [n: number]: string }) {
    console.log(yourEnum[value]);
}
// Would-be error, dbgPrint wants mutable index signature but enum has readonly signature
dbgPrint(SyntaxKind.OtherNode, SyntaxKind);

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jan 19, 2016

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Ah right, because of the assignability rule. Makes sense.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Jan 19, 2016

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

That's right. It's bit of a puzzle to ensure backwards compatibility, so there are actually two forms of read-only-ness in the new implementation:

  • readonly properties, readonly index signatures, and get only accessors are always considered read-only, in both assignments and type compatibility checks.
  • const variables, functions, classes, namespaces, enums, and enum members are considered read-only in assignments, but not in type compatibility checks. This ensures backwards compatibility. For example, it is reasonably common to implement an interface contract using a module or namespace, and strict read-only-ness would break that pattern.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jan 20, 2016

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I see. That sounds like a good distinction.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Anders Hejlsberg (ahejlsberg) added 4 commits January 19, 2016 09:45
@ahejlsberg
Chaning "read-write" to "writable" in error messages
dc8ab95
@ahejlsberg
Accepting new baselines
4abf717
@ahejlsberg
Add ReadonlyArray<T> to core.d.ts
cac3a32
@ahejlsberg
Consider ReadonlyArray<T> an array-like type
8bb00fe
yuit
yuit reviewed Jan 20, 2016
View reviewed changes
Comment thread tests/baselines/reference/decrementOperatorWithEnumType.errors.txt

@yuit Yui (yuit) Jan 20, 2016

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

As clarification, increment/decrement operator and any assignment to enum isn't allowed as enum type is read-only upon assignment? if that is the case, I think the error message should be updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Jan 20, 2016
Add readonly modifier to DOM declarations microsoft/TypeScript-DOM-lib-generator#55
Closed
Anders Hejlsberg (ahejlsberg) added 5 commits January 20, 2016 16:51
@ahejlsberg
Make get only accessor compatible with writable property in type rela…
fc6947b
…tionships
@ahejlsberg
Fixing fourslash test
9562353
@ahejlsberg
Accepting new baselines
b6f43e6
@ahejlsberg
Disallow assigments to exported variables from external modules
fac1bf6
@ahejlsberg
Accepting new baselines
e506378
@joelday

Joel Day (joelday) commented Jan 22, 2016

Copy link
Copy Markdown
Contributor

Just stumbled upon this. Don't think I could be any happier than I am right now.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request May 6, 2016
A readonly class property that overrides a non-readonly property can still be reassigned in the base class (and vice-versa) #8496
Closed
@malibuzios

malibuzios commented May 7, 2016

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) These questions in #8496 (and the issue in general) are highly relevant to the topic here. Consider providing your comments. I have only closed that issue because I felt that your (unbelievably stubborn and righteous) team members would somehow find it more 'respectful' that I show my complete pessimism at having any influence here than trying to 'confront' them in any way. You have already won the 'battle', congratulations, now feel free to join the discussion.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@s-panferov Stanislav Panferov (s-panferov) mentioned this pull request May 7, 2016
Readonly properties and index signatures s-panferov/tygen#35
Closed
@joelday

Joel Day (joelday) commented May 8, 2016

Copy link
Copy Markdown
Contributor

Unbelievably stubborn and righteous, eh?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@malibuzios

malibuzios commented May 8, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

[I apologize for this being very off-topic but since I was asked I felt I had no choice but answering here, the person who asked doesn't have a public email so there is no way for me to contact them personally]

Joel Day (@joelday)

Yes, my personal impression is that they are very 'difficult' people to deal with, in the sense that they are putting as much energy into 'justifying' (sometimes dishonestly or irrationally) their actions and decisions to actually considering things openly and objectively when presented to them. They even do this on some very trivial subjects (e.g. you tell them they don't conform to the ES6 spec by practically disallowing inheritance of static accessors for ES5 targets and instead of listening and acknowledging the problem like a normal person would do, a group of team members just 'jumps' on you and starts to throw some strange and arbitrary ('canonical') justifications about performance consideration and dubious statistics there is '0%' anyone would ever want to do this.. well I did! and so did others who reported this problem, and probably many who didn't.. :( ).

They put you in a situation where you have to be so eloquent and rational, to the point you have to literally spend dozens of hours into very carefully and rationally crafting a 'thesis' for something otherwise relatively trivial, just for them. And they on the other side are allowed to simply 'push' their opinions and twisted patterns of 'justification' arbitrarily without anyone saying anything. When you start challenging those patterns they would either ignore you or tell you you are intentionally 'wasting their time' with things that aren't important. They would also use their 'internal' understanding of the language and compiler by mentioning intricate things or concepts you don't know or completely understand and dishonestly use that against you.

They take submitted ideas they like but they don't actually partake in real two-way conversations about it. They don't really involve the submitter of their personal ideas and insights, at least before they decide on their actions. And when/if they do inform back, they would condense it to a set of a few sentences that are not very useful or enlightening by themselves (or direct you to a set of design 'notes' that are very hard to extract anything useful from). You can't ask a (non-trivial) question because that's just 'wasting their time' (they don't do 'one on one').

If they don't like an idea, they would usually 'tank' and attack it mercilessly to the point of almost (or perhaps actually) insulting the person who submitted it. They almost never 'improve' on things suggested to them, at least not publicly. They would rather spend their time and 'smarts' in crafting careful arguments for justifying why they think someone are 'wrong' and they must be 'right' than taking a step back to seriously and objectively reconsider things, and eventually thank the participant for contributing something useful to the discussion.

This has been my experience so far, anyway. I'm mostly referring here to the 2-3 members who reply to GitHub issues.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RReverser

Ingvar Stepanyan (RReverser) commented May 9, 2016

Copy link
Copy Markdown
Contributor

Is there any reason / chance to change that the generated code contains regular property assignment and not defineProperty with writable: false? That would help a lot to protect against runtime assignment to properties designed as readonly.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Robert923

Robert923 commented May 9, 2016

Copy link
Copy Markdown

I agree with Ingvar Stepanyan (@RReverser) - on my team we frequently use TypeScript compiled code from (legacy)JavaScript applications - so keeping as many of those protections as possible would be very helpful.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ScallyGames

Pascal Blessing (ScallyGames) commented Jun 10, 2016

Copy link
Copy Markdown

Is this already available or only for TypeScript 2.0 (neither VS nor TypeScript Playground seem to understand it)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RReverser

Ingvar Stepanyan (RReverser) commented Jun 10, 2016

Copy link
Copy Markdown
Contributor

@Aides359 As usual with all the fresh stuff, you need to use npm i typescript@next to get the latest compiler with experimental features included.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aluanhaddad

Aluan Haddad (aluanhaddad) commented Jun 11, 2016

Copy link
Copy Markdown
Contributor

@Aides359 I think the playground hosts the latest stable release. I recommend doing what Ingvar Stepanyan (@RReverser) suggests and additionally installing vscode and setting then "typescript.tsdk" property of your user/workspace settings to the install location's lib directory.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zspitz Zev Spitz (zspitz) mentioned this pull request Jul 17, 2016
Add a page (or pages) with a list of keywords, and links to documentation microsoft/TypeScript-Handbook#341
Open
@paleo

Thomas Mur (paleo) commented Jul 25, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.

Why disallow assignments in the other methods of a class?

A readonly property would be useful to implement a getter with a true property (better for performance, readability):

class Foo {
    readonly a = 1;
    inc() {
        ++this.a;  // Why not allow to do this?
    }
}

Does the limitation exist for a technical reason?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Jul 25, 2016

Copy link
Copy Markdown
Member

If that were allowed we'd have daily reports about "Bug: Why does TS let me mutate readonly fields?" 😉

We discussed the notion of "public readonly, protected mutable" but opted not to simply due to complexity. You can already have a getter-only wrapper around a private variable, which achieves the same semantics at the cost of slightly increased class size.

tp, aluanhaddad, paleo, and SlurpTheo reacted with thumbs up emoji
All reactions
  • 👍 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@paleo

Thomas Mur (paleo) commented Jul 25, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

OK. You use the term readonly as a synonym for const. I hoped an accessor readonly. :(

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Aug 1, 2016
Suggestion: Use the keyword "readonly" as an accessor for classes #10064
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 8, 2016
Always use literal types #10676
Merged
@yortus Troy Gerwien (yortus) mentioned this pull request Sep 14, 2016
TypeScript should have "Records" like C#7 #10910
Closed
@shiwano Shogo Iwano (shiwano) mentioned this pull request Sep 23, 2016
Bump TypeScript version to 2.0 shiwano/typhen#5
Closed
6 tasks done
@HerringtonDarkholme Herrington Darkholme (HerringtonDarkholme) mentioned this pull request Oct 10, 2016
Suggestion: Infer literal types for getter only properties that return literals #11467
Closed
@ghost

Deleted user (ghost) commented Dec 10, 2016

Copy link
Copy Markdown

I use this great feature for mutable preventing by the syntax highlighting. Thanks a lot for this!

+++

joelday reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Dec 17, 2016
add enforceReadonlyAssignability flag for smoother transition to true readonly modifiers #13002
Closed
@zpdDG4gta8XKpMCd

ZpdDG4gta (zpdDG4gta8XKpMCd) commented Dec 17, 2016

Copy link
Copy Markdown

This change isn't as bad as it might sound because we'll always error on direct assignments to readonly properties and that's where the majority of the errors occur.

this is a horrible change, rendering this feature usless

Ailrun reacted with thumbs up emoji alex-kinokon and joelday reacted with thumbs down emoji
All reactions
  • 👍 1 reaction
  • 👎 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zpdDG4gta8XKpMCd

ZpdDG4gta (zpdDG4gta8XKpMCd) commented Dec 17, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

simple use case that fails, I have my app about 1500 TS files thick, in this app I have an interface named Data that was intended to be immutable historically could not be forced being so

With the readonly feature announced it was immediately clear that such enforcment can be imposed.

However it turned out that there were a few places where Data was meant to be mutable.

To address it the original Data interface was split into ReadOnlyData and WitableData, however it didn't help much due to the said decision. Details can bee seen in #13002.

Bottom line is we are still not sure if Data is effectively mutable or not, despite applibg readonly to all its properties.

maxlk and Ailrun reacted with thumbs up emoji alex-kinokon and joelday reacted with thumbs down emoji
All reactions
  • 👍 2 reactions
  • 👎 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@masaeedu

Asad Saeeduddin (masaeedu) commented Dec 19, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg) Correct me if I'm wrong, but the only reason interface compatibility checking on readonly is a breaking change is because of readonly being implicitly applied to everything. The tradeoff of worse soundness in return for saving the user some typing, especially when you now have mapped types, is not a good one IMO.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielearwicker Daniel Earwicker (danielearwicker) mentioned this pull request Jan 7, 2017
Interface with readonly property is assignable to interface with mutable property #13347
Open
@dilijev Doug Ilijev (dilijev) mentioned this pull request Apr 11, 2017
[typescript/en] Include examples involving readonly adambard/learnxinyminutes-docs#2702
Closed
@kitsonk Kitson Kelly (kitsonk) marked this as a duplicate of #17181 Jul 14, 2017
@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Jul 14, 2017
Add pure and immutable keywords to ensure code has no unintended side-effects #17181
Open
@olegdunkan olegdunkan mentioned this pull request Sep 11, 2017
Generic Function with Indexed Type ignores readonly #18374
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

None yet

Projects

None yet

Milestone

TypeScript 2.0

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

20 participants

@ahejlsberg @JsonFreeman @joelday @RyanCavanaugh @simonbuchan @basarat @myitcv @jpablo @danquirk @aluanhaddad @Robert923 @electricessence @malibuzios @RReverser @ScallyGames @paleo @zpdDG4gta8XKpMCd @masaeedu @yuit @DanielRosenwasser
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.