typescript - 如何强制 Set 对象包含枚举中的所有值 - TS
问题描述
我有以下枚举:
enum GREETINGS { HELLO = 'Hello', HI = 'Hi' }
我想为 Set 数据结构创建一个类型,该类型将强制用户在其中包含所有枚举值,如下所示:
type MyGreetings = Set<GREETINGS>
预期结果应该会突出显示以下行,并发出警告,指出并非所有必需的值都包含在结果中:
const result: MyGreetings = new Set([GREETINGS.HELLO]);
解决方案
一个问题是 TypeScript 认为Set<GREETINGS.HELLO>
可以分配给 / 兼容 Set<GREETINGS>
; 从理论上讲,这应该意味着如果你想Set<GREETINGS>
被分配给MyGreetings
,那么通过可分配性的传递性, Set<GREETINGS.HELLO>
也将被分配给MyGreetings
,并且你在开始之前就停止了。但:
Set<GREETINGS.HELLO>
可分配的事实实际上是Set<GREETINGS>
在类型系统中使用了一个故意的漏洞:方法参数 bivariance。方法参数双变量非常有用,但它不是类型安全的:
const justHello = new Set<GREETINGS.HELLO>([GREETINGS.HELLO]);
const stillJustHello: Set<GREETINGS> = justHello;
stillJustHello.add(GREETINGS.HI) // <-- no error, oops
通过“扩大”justHello
到stillJustHello
,编译器忘记了它不应该接受除 之外的任何成员GREETINGS.HELLO
。
因此,更接近您想要的一种方法是使用编译--strictFunctionType
器选项(它是编译器选项套件的一部分--strict
Set
,通常建议使用)并使用函数属性类型而不是方法签名重写至少一个方法签名。让我们看一下add()
,看看有什么区别:
type Method = { add(x: GREETINGS): void } // method syntax
const m: Method = new Set<GREETINGS.HELLO>(); // okay
type FuncProp = { add: (x: GREETINGS) => void } // function syntax
const f: FuncProp = new Set<GREETINGS.HELLO>(); // error
所以让我们尝试一下:
type MyGreetings = Set<GREETINGS> & { add: (g: GREETINGS) => MyGreetings };
如果我们使用它,您将获得所需的结果:
const result: MyGreetings = new Set([GREETINGS.HELLO]); // error!
// Type 'GREETINGS' is not assignable to type 'GREETINGS.HELLO'
const okay: MyGreetings = new Set([GREETINGS.HELLO, GREETINGS.HI]); // okay
万岁!
嗯,有点。编译器确实无法确切知道哪些值已传递到Set
. 它可以推断出某些类型new Set(...)
,但有一些方法可以手动指定更宽的值或让编译器推断出更宽的值。然后上面的所有进展都丢失了。Set<X>
X
X
X
例如,由于Set<GREETINGS>
允许 a 仅包含 的子集GREETINGS
,因此可以执行以下手动规范:
const oops: MyGreetings = new Set<GREETINGS>([GREETINGS.HELLO]) // no error
或者有人可以让编译器以这种方式推断联合:
const alsoOops: MyGreetings = new Set([
Math.random() < 0.5 ? GREETINGS.HELLO : GREETINGS.HI
]); // no error
这些都不是错误。等号右侧的值为 a Set<GREETINGS>
。而且您需要Set<GREETINGS>
可分配给MyGreetings
,因此分配也不是错误。对此无能为力。
如果在实践中您不认为有人会以这些或其他病态方式创建集合,那么也许您可以使用MyGreetings
. 否则,如果你真的需要保证某些东西,你应该考虑以某种方式改变你的设计。但这可能超出了问题的范围,所以我会停在那里。
推荐阅读
- spartacus-storefront - 如何禁用斯巴达克斯默认呼叫
- excel - Excel - 在 FilterXML 中添加 Excel 公式
- excel - 选中一个复选框时如何取消选择所有其他复选框
- google-cloud-platform - Azure 策略的 GCP 等效项
- azure - 为 Azure Key Vault 配置灾难恢复和自动故障转移?
- javascript - Bot 在用户加入时不会向用户发送消息
- java - Eclipse 中的 Maven 构建任务问题 - StackOverflow 和 DeferredTypeMap 的递归调用?
- file-upload - Angular 7 正确上传 doc/docx 文件
- file - 如果我将数据库文件和其他用户创建的文件存储在 TimesTen 实例树中,可以吗?
- python - 在 Windows 中安装 DLIB、TensorFlow 和人脸识别错误