typescript - 如何确保只读属性不可分配给可变属性?
问题描述
假设我在 TypeScript 中有两种类型,A
并且B
我不希望类型A
的值可以分配给B
.
我想强制执行这一点,这样如果一个(或两个)类型被意外修改以允许分配,我们会得到编译错误或测试失败。有没有办法在 TypeScript 中实现这一点?
当然,只要在测试中进行分配,就很容易强制执行分配是合法的。但我无法立即看到强制特定代码不通过TypeScript 类型检查的方法。
以下是有关我为什么要这样做的更多背景信息。我想强制执行特定类型的不变性,如下所示:
interface MyImmutableThing {
readonly myfield: string;
}
但是,这个问题会造成问题,因为如果我有一个像这样的其他相同的可变类型:
interface MyThing {
myfield: string;
}
然后 typeMyImmutableThing
的值可以分配给MyThing
,从而允许绕过和改变类型安全myfield
。以下代码编译、运行并导致imm.myfield
更改:
const imm: MyImmutableThing = {myfield: 'mumble'};
const mut: MyThing = imm;
mut.myfield = 'something else';
我不知道如何在编译时稳健地确保此类类型的不变性,但我至少可以通过使用类来实现运行时强制,如下所示:
class MyImmutableThing {
private _myfield: string;
get myfield(): string { return this._myfield; }
constructor(f: string) { this._myfield = f; }
}
然后,虽然像下面这样的代码仍然可以编译,但它会导致运行时错误:
const imm = new MyImmutableThing('mumble');
const mut: MyThing = imm;
mut.myfield = 'something else';
然后我可以编写一个测试来断言发生此运行时错误。
但是,如果我的字段是数组(或元组)类型,情况会发生变化:
interface MyArrayThing {
myfield: string[];
}
interface MyImmutableArrayThing {
readonly myfield: readonly string[];
}
现在,由于数组类型的特性, 的值MyImmutableArrayThing
不能分配给。以下将无法编译:MyArrayThing
readonly
const imm: MyImmutableArrayThing = {myfield: ['thing']};
const mut: MyArrayThing = imm;
这很好,因为它为我们提供了比我们在该string
领域获得的更多的编译时不变性保证。然而,现在更难编写测试来捕捉我们的意图,或者以其他方式强制执行它。
MyImmutableArrayThing
s to的不可分配性MyArrayThing
是强制执行我们想要的属性的类型系统的关键,但是我们如何阻止某人进行一些更改,例如添加readonly
到数组 in MyArrayThing
,允许这样的事情并破坏我们想要的属性?
interface MyArrayThing {
myfield: readonly string[]; // now readonly
}
interface MyImmutableArrayThing {
readonly myfield: readonly string[];
}
const imm: MyImmutableArrayThing = {myfield: ['thing']};
const mut: MyArrayThing = imm;
mut.myfield = ['other thing'];
目前, TypeScript 的readonly
执行相当混乱,因此能够做出这种断言将非常有助于防止回归。
解决方案
通过使用一种称为“类型标记”的技术,您可以实现“名义类型”,即尽管共享相同的结构,但使相似类型不兼容。
请查看TypeScript 游乐场中的此示例以了解更多详细信息。
在您的情况下,类型品牌可能如下所示:
interface ImmutableThing {
readonly myfield: string
__brand: "ImmutableThing"
}
interface MutableThing {
myfield: string
__brand: "MutableThing"
}
const imm: ImmutableThing = {myfield: "thing"} as ImmutableThing;
const mut: MutableThing = imm; // type error
mut.myfield = "mutated";
如果您对类型品牌感兴趣,请查看ts-brand了解更高级的用法。
推荐阅读
- python - 熊猫相关矩阵迭代
- apache-spark - 如何将行添加到 Spark 中的现有分区?
- javascript - 如何在javascript中多次读取同一个文件
- windows - Azure 批处理 Windows 容器工作负载从提供的命令行中删除引号
- elasticsearch - 基于 3 个不同的弹性搜索查询聚合结果
- java - 无法在 Android 中正确设置改造标头
- python - Pandas json_normalize 和 JSON 扁平化错误
- javascript - 如何从我使用选择选项的数据库中获取数据,以便它可以显示在输入框中
- out-of-memory - 如何增加 WEKA 中的堆大小?
- javascript - THREE.OrbitControls 不是构造函数,不知道该怎么做