如何强制 Set 对象包含枚举中的所有值 - TS

Ovi*_*nas 3 enums set typescript

我有以下枚举:
enum GREETINGS { HELLO = 'Hello', HI = 'Hi' }

我想为 Set 数据结构创建一个类型,该类型将强制用户在其中包含所有枚举值,如下所示:
type MyGreetings = Set<GREETINGS>

预期结果应导致以下行突出显示,并带有警告,指出结果中并未包含所有必需的值:
const result: MyGreetings = new Set([GREETINGS.HELLO]);

jca*_*alz 5

一个问题是 TypeScript 认为Set<GREETINGS.HELLO>可以分配给/兼容 Set<GREETINGS>; 从理论上讲,这应该意味着如果您想Set<GREETINGS>可分配给MyGreetings,那么通过可分配性的 传递性Set<GREETINGS.HELLO>,也将可分配给MyGreetings,并且您在开始之前就停止了。但:

Set<GREETINGS.HELLO>可分配的事实Set<GREETINGS>实际上是使用类型系统中的一个故意的漏洞:方法参数双变量。方法参数双变非常有用,但它不是类型安全的:

const justHello = new Set<GREETINGS.HELLO>([GREETINGS.HELLO]);
const stillJustHello: Set<GREETINGS> = justHello;
stillJustHello.add(GREETINGS.HI) // <-- no error, oops
Run Code Online (Sandbox Code Playgroud)

通过“扩大”justHellostillJustHello,编译器忘记了它不应该接受除 之外的任何成员GREETINGS.HELLO

因此,更接近您想要的效果的一种方法是使用编译--strictFunctionType器选项(它是编译器选项套件--strictSet一部分,一般建议使用)并使用函数属性类型而不是方法签名重写至少一个方法签名。让我们看一下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
Run Code Online (Sandbox Code Playgroud)

那么让我们尝试一下:

type MyGreetings = Set<GREETINGS> & { add: (g: GREETINGS) => MyGreetings };
Run Code Online (Sandbox Code Playgroud)

如果我们使用它,您将得到所需的结果:

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
Run Code Online (Sandbox Code Playgroud)

万岁!


嗯,有点。编译器确实无法确切知道哪些值已传递到Set. 它可以推断出这是某些类型new Set(...)的类型,但是有一些方法可以手动指定更宽的类型或让编译器推断更宽的类型。然后上面的所有进度都会丢失。Set<X>XXX

例如,由于Set<GREETINGS>允许 a 仅包含 的子集GREETINGS,因此有人可以执行以下手动规范:

const oops: MyGreetings = new Set<GREETINGS>([GREETINGS.HELLO]) // no error
Run Code Online (Sandbox Code Playgroud)

或者有人可以让编译器以这种方式推断联合:

const alsoOops: MyGreetings = new Set([
  Math.random() < 0.5 ? GREETINGS.HELLO : GREETINGS.HI
]); // no error
Run Code Online (Sandbox Code Playgroud)

这些都不是错误;等号右侧的值是 a Set<GREETINGS>。并且您需要Set<GREETINGS>可分配给MyGreetings,因此分配也不会出现错误。对此我们无能为力。

如果在实践中你认为有人不会以这些或其他病态的方式创建一个集合,那么也许你可以使用MyGreetings. 否则,如果你确实需要保证某些东西,你应该考虑以某种方式改变你的设计。但这可能超出了问题的范围,所以我就到此为止。


Playground 代码链接(已测试并适用于 typescript 版本 3.5.1 及更高版本)