Skip to content
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

[Suggestion] Compiler Flag to treat types as immutable #16317

Open
battmanz opened this issue Jun 7, 2017 · 4 comments
Open

[Suggestion] Compiler Flag to treat types as immutable #16317

battmanz opened this issue Jun 7, 2017 · 4 comments
Labels
Add a Flag Any problem can be solved by flags, except for the problem of having too many flags Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@battmanz
Copy link

battmanz commented Jun 7, 2017

TypeScript Version: 2.3.4

Code

I'm aware of #10725, but I want to take it one step further and add a compiler flag (e.g. --immutable) that will cause the compiler to infer Readonly, ReadonlyArray, ReadonlyMap, and ReadonlySet (and any other data structures I've missed) everywhere. The flag will also force you to use const. Thus eliminating mutability from the language.

// tsconfig.json

{
  "compilerOptions": {
    "immutable": true
  }
}
// demo.ts

// band is automatically inferred as:
// Readonly<{
//   name: string;
//   members: ReadonlyArray<{
//     name: string;
//     instruments: ReadonlyArray<string>;
//   }>;
// }>
const band = {
  name: 'U2',
  members: [
    {
      name: 'Bono',
      instruments: ['vocals', 'rhythm guitar', 'harmonic']
    },
    {
      name: 'The Edge',
      instruments: ['lead guitar', 'keyboards', 'backing vocals']
    },
    // ...
  ]
};

// The following will cause compiler errors:
// band.name = 'Green Day';
// band.members.push({ name: 'Billie Joe Armstrong', instruments: ['lead vocals', 'guitar'] });

// ----------------------------------------------------------------------

// numberMap is automatically inferred to be ReadonlyMap<number, string>
const numberMap = new Map<number, string>([
  [1, 'one'],
  [2, 'two']
]);

// Compiler error:
// numberMap.set(3, 'three');

// letterSet is is automatically inferred to be ReadonlySet<string>
const letterSet = new Set<string>(['A', 'B', 'C', 'D']);

// Compiler error:
// letterSet.add('E');
@battmanz battmanz changed the title Feature Request: Compiler Flag to infer Readonly<T>, ReadonlyArray, ReadonlyMap, ReadonlySet everywhere! [Suggestion] Compiler Flag to infer Readonly<T>, ReadonlyArray, ReadonlyMap, ReadonlySet everywhere! Jun 7, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Jun 7, 2017

what about all the other types?

@battmanz
Copy link
Author

battmanz commented Jun 8, 2017

@mhegazy good question!

Primitives
If I understand correctly, all the primitives are inherently immutable. So those wouldn't need to change. They include:

  1. boolean
  2. null
  3. undefined
  4. number
  5. string
  6. symbol

However, I have discovered that I can mess things up pretty good by either (1) shadowing the prototype method such as valueOf in the example below or (2) reassigning the prototype method such as startsWith in the example below.

// Shadow the valueOf method
const b = new Boolean(true);
b.valueOf = () => false;
console.log(b.valueOf()); // false

// Reassign the startsWith method
String.prototype.startsWith = () => true;
const s = 'hello';
console.log(s.startsWith('watermelon')); // true

So perhaps we need to define the following interfaces: ReadonlyBoolean, ReadonlyString, ReadonlyNumber, etc. The compiler can infer those in order to prevent us from monkeying with the prototype. Thoughts?

Built-in Objects
In addition to the primitive types, there are also the standard built-in objects. Honestly, for my use case, I don't need a ReadonlyFloat32Array, ReadonlyFloat64Array, etc. Nor do I need a ReadonlySIMDBool16x8 or ReadonlySIMDInt16x8, etc.

For me it would be enough to have readonly versions of the following (just going down the list in MDN):

  1. Array (*)
  2. Boolean
  3. Date
  4. Error (all flavors: EvalError, InternalError, etc)
  5. Function
  6. Intl
  7. JSON
  8. Map (*)
  9. Math
  10. Number
  11. Object (*)
  12. Promise
  13. Proxy
  14. Reflect
  15. RegExp
  16. Set (*)
  17. String
  18. Symbol
  19. WeakMap
  20. WeakSet
  • = A readonly interface already exists.

I know it would be a bit tedious to go through and create readonly interfaces for all of those objects, but it's not impossible. I could probably do that and submit a pull request. The real work would be adding the flag to cause the compiler to infer it.

Motivation
You can probably already guess, but my motivation for wanting this feature is to encourage and support a functional programming paradigm. It would be really great to get compiler support. I know that libraries like immutable.js exist, but it causes me to use custom data structures instead of the data structures built into the language. An immutable List, for example, can't be used with libraries like ramda or lodash.

@battmanz
Copy link
Author

@mhegazy I was thinking about this some more. Honestly, I don't think we need to worry about making the prototype immutable on all of those types I mentioned above. In practice that just doesn't come up. For my use case, I would simply need immutable objects, arrays, maps, sets, and maybe dates. seamless-immutable does NOT make dates immutable, so dates are debatable.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 13, 2017

I think a better approach is to consider all types immutable by default; mutable types should be marked as such. related to #10725.
obviously it is not as simple as that, we need a way to mark methods as mutating/non-mutating (e.g. Array.prototype.push).

Readonly<T> does not do the something.

@mhegazy mhegazy added the Suggestion An idea for TypeScript label Jun 13, 2017
@mhegazy mhegazy changed the title [Suggestion] Compiler Flag to infer Readonly<T>, ReadonlyArray, ReadonlyMap, ReadonlySet everywhere! [Suggestion] Compiler Flag to treat types as immutable Jun 13, 2017
@RyanCavanaugh RyanCavanaugh added Add a Flag Any problem can be solved by flags, except for the problem of having too many flags Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Aug 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Add a Flag Any problem can be solved by flags, except for the problem of having too many flags Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants