如何强制 Set 对象包含枚举中的所有值 - TS
How to force a Set object to contain all values within an enum - TS
我有以下枚举:
enum GREETINGS { HELLO = 'Hello', HI = 'Hi' }
我想为 Set 数据结构创建一个类型,它会强制用户在其中包含所有枚举值,如下所示:
type MyGreetings = Set<GREETINGS>
预期的结果应该会导致以下行突出显示,并带有一条警告,指出结果中并未包含所有必需的值:
const result: MyGreetings = new Set([GREETINGS.HELLO]);
一个问题是 TypeScript 认为 Set<GREETINGS.HELLO>
是 assignable to / compatible with Set<GREETINGS>
; in theory that should mean that if you want Set<GREETINGS>
to be assignable to MyGreetings
, then by transitivity 的可分配性,Set<GREETINGS.HELLO>
也将可分配给 MyGreetings
,你在你之前就停止了'已经开始了。但是:
Set<GREETINGS.HELLO>
可分配给 Set<GREETINGS>
的事实实际上是在类型系统中使用了一个有意的漏洞:method parameter 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
.
之外的任何成员
因此,一种更接近您想要的方法是使用 the --strictFunctionType
compiler option (which is part of the --strict
suite of compiler options and is recommended in general) and rewrite at least one Set
method signature using a function property type 而不是方法签名。让我们看看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(...)
对于某些 X
是 Set<X>
类型,但是有一些方法可以手动指定更宽的 X
或让编译器推断出更宽的 X
。然后上面的所有进度都丢失了。
例如,由于允许 Set<GREETINGS>
仅包含 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
这些都不是错误;等号右边的值是 Set<GREETINGS>
。而且你需要 Set<GREETINGS>
可以分配给 MyGreetings
,所以分配也不是错误。对此无能为力。
如果在实践中您不认为有人会以这些或其他病态方式创建集合,那么也许您可以使用 MyGreetings
。否则,如果你真的需要保证某些东西,你应该考虑以某种方式改变你的设计。但这可能超出了问题的范围,所以我就到此为止。
我有以下枚举:
enum GREETINGS { HELLO = 'Hello', HI = 'Hi' }
我想为 Set 数据结构创建一个类型,它会强制用户在其中包含所有枚举值,如下所示:
type MyGreetings = Set<GREETINGS>
预期的结果应该会导致以下行突出显示,并带有一条警告,指出结果中并未包含所有必需的值:
const result: MyGreetings = new Set([GREETINGS.HELLO]);
一个问题是 TypeScript 认为 Set<GREETINGS.HELLO>
是 assignable to / compatible with Set<GREETINGS>
; in theory that should mean that if you want Set<GREETINGS>
to be assignable to MyGreetings
, then by transitivity 的可分配性,Set<GREETINGS.HELLO>
也将可分配给 MyGreetings
,你在你之前就停止了'已经开始了。但是:
Set<GREETINGS.HELLO>
可分配给 Set<GREETINGS>
的事实实际上是在类型系统中使用了一个有意的漏洞:method parameter 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
.
因此,一种更接近您想要的方法是使用 the --strictFunctionType
compiler option (which is part of the --strict
suite of compiler options and is recommended in general) and rewrite at least one Set
method signature using a function property type 而不是方法签名。让我们看看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(...)
对于某些 X
是 Set<X>
类型,但是有一些方法可以手动指定更宽的 X
或让编译器推断出更宽的 X
。然后上面的所有进度都丢失了。
例如,由于允许 Set<GREETINGS>
仅包含 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
这些都不是错误;等号右边的值是 Set<GREETINGS>
。而且你需要 Set<GREETINGS>
可以分配给 MyGreetings
,所以分配也不是错误。对此无能为力。
如果在实践中您不认为有人会以这些或其他病态方式创建集合,那么也许您可以使用 MyGreetings
。否则,如果你真的需要保证某些东西,你应该考虑以某种方式改变你的设计。但这可能超出了问题的范围,所以我就到此为止。