typescripttsconfig

Is there a typescript compiler option that prevents implicit widening from readonly properties to read/write?


By default, typescript treats readonly object properties as satisfying standard read/write properties during type checking. This happens without type assertions or any other trickery. This is a surprising behavior, and can lead to runtime problems. Is there any way to turn this off using a compiler option? Am I doing this wrong somehow?

Here's an example of what I'm talking about.

type O = { p: number; };
const r: O = { get p() { return 2; } };

// Passes type checking, but fails at run time.
// Uncaught TypeError: setting getter-only property "p"
r.p = 3; 


Solution

  • This is a longstanding known soundness issue, tracked at microsoft/TypeScript#13347. Currently a readonly property will prevent you from writing to it directly, but it is still considered assignable both to and from a non-readonly property. This assignability was made so that the feature wouldn't be a huge breaking change for existing code. It's not completely useless like this, but it's surprising enough that another issue about the same topic, microsoft/TypeScript#13002, was originally given the title "readonly modifiers are a joke".

    As of TypeScript 5.7 there is no compiler option to address this, in any released version of the language. However there is an --enforceReadonly flag implementation at the pull request microsoft/TypeScript#58296, written by the TypeScript language architect. So there's a good chance this will become part of the language at some point in the near-ish future. In the design meeting notes at microsoft/TypeScript#59406 and the TypeScript 5.7 iteration plan at microsoft/TypeScript#58296 it looks like they were considering implementing this with TypeScript 5.7, but it doesn't seem to have happened. I don't see an iteration plan for TypeScript 5.8, so I'm not sure if it will be part of that version either. Maybe TypeScript 5.9? Certainly if it does get released, it will still be a fairly big breaking change for anyone who enables it, which is why it's --enforceReadonly and not --strictReadonly; it won't be part of the --strict suite of compiler options.

    Until and unless it is merged, you'll just have to deal with it, and even if it is merged, you might not be able to use that compiler option right away if you depend on any libraries that it breaks. For now, you should just be careful with readonly properties.