TypeScript 建议:将类型视为不可变的编译器标志

xpcnnkqh  于 6个月前  发布在  TypeScript
关注(0)|答案(4)|浏览(67)

TypeScript版本: 2.3.4
代码

我知道 #10725 ,但我想进一步实现,添加一个编译器标志(例如 --immutable )使编译器推断出 ReadonlyReadonlyArrayReadonlyMapReadonlySet(以及我可能遗漏的其他数据结构)。该标志还将迫使您使用 const。从而消除了语言中的可变性。

// 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');
2w2cym1i

2w2cym1i2#

原始类型

如果我理解正确,所有的原始类型都是不可变的。所以它们不需要改变。它们包括:

  1. boolean
  2. null
  3. undefined
  4. number
  5. string
  6. symbol
    然而,我发现通过(1)在下面的例子中覆盖原型方法或者(2)重新分配原型方法,我可以很好地破坏事情。
// 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

所以也许我们需要定义以下接口:ReadonlyBoolean, ReadonlyString, ReadonlyNumber等。编译器可以推断出这些接口,以防止我们捣乱原型。大家有什么想法吗?

内置对象

除了原始类型之外,还有standard built-in objects。老实说,对于我的用例,我不需要ReadonlyFloat32Array, ReadonlyFloat64Array等。我也不需要ReadonlySIMDBool16x8ReadonlySIMDInt16x8等。
对我来说,只要有以下只读版本就足够了(只是按照MDN的顺序列出):

  1. Array(*)
  2. Boolean
  3. Date
  4. Error(所有口味:EvalError,InternalError等)
  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
  • =已经存在一个只读接口。

我知道要遍历并为所有这些对象创建只读接口会有点繁琐,但这并不是不可能的。我可能可以做到这一点并提交拉取请求。真正的工作是添加标志来引起编译器的推断。

动机

你可能已经猜到了,我想要这个功能的原因是为了鼓励和支持函数式编程范式。如果有编译器支持的话会非常好。我知道像immutable.js这样的库已经存在,但是它导致我使用自定义数据结构而不是语言内置的数据结构。例如,一个不可变的List不能与像ramdalodash这样的库一起使用。

sqxo8psd

sqxo8psd3#

@mhegazy 我再思考了一下这个问题。老实说,我认为我们不需要担心在上面提到的所有类型上使prototype不可变。实际上,这并不会出现。对于我的用例,我只需要不可变的对象、数组、Map、集合,以及可能的日期。seamless-immutable 不会使日期不可变,所以日期是有争议的。

vtwuwzda

vtwuwzda4#

我认为更好的方法是默认将所有类型视为不可变;可变类型应该被标记为如此。与 #10725 相关。
显然,这并不像那么简单,我们需要一种方法来标记方法为可变/不可变(例如 Array.prototype.push )。
Readonly<T> 没有做这件事。

相关问题