我试图根据满足switch
条件的enum
值自动推断在case
语句中传递的某些数据的类型。
为此,我定义了一个const枚举:
const enum MESSAGES {
open = 1,
close,
redo
}
然后我用它来索引Interface
:
interface MessagePayloadContent {
[MESSAGES.open]: string;
[MESSAGES.close]: number;
[MESSAGES.redo]: boolean;
}
这时,我定义了将在switch
语句中评估的对象:
interface MessagePayload<T extends MESSAGES> {
scope: T;
content: MessagePayloadContent[T];
}
最后,我在switch语句中使用了以上内容。
我希望解释器可以基于传递给每个case
的值来推断object
中将包含什么类型的数据。
相反,出现以下代码中的注释中的错误:
function pick(payload: MessagePayload<MESSAGES>): void {
switch (payload.scope) {
case MESSAGES.open:
open(payload.content); // Argument of type 'string | number | boolean' is not assignable to parameter of type 'string'.
break;
case MESSAGES.close:
close(payload.content as number); // This is my current workaround.
break;
case MESSAGES.redo:
redo(payload.content); // Argument of type 'string | number | boolean' is not assignable to parameter of type 'boolean'.
break;
}
}
const open = (d: string) => d;
const close = (d: number) => d;
const redo = (d: MessagePayloadContent[MESSAGES.redo]) => d;
我不能完全理解的是,在某些其他情况下,这种方法确实有效,所以我想知道为什么在其他情况下,这种方法不起作用。
答案 0 :(得分:1)
这里的问题是类型MessagePayload<MESSAGES>
不是您认为的那样。接口通常不会在联合上分布,因此其求值结果为:
interface OopsMessagePayload {
scope: MESSAGES;
content: string | number | boolean;
}
这意味着,如果您检查scope
属性,它不会缩小content
属性的类型。
TypeScript确实具有一些通过联合分布的类型级别的构造,因此应该有一种定义
的方法。type MessagePayloadDistributive<T> = ...
如此
MessagePayloadDistributive<MESSAGES>
评估为
MessagePayload<MESSAGES.open> |
MessagePayload<MESSAGES.closed> |
MessagePayload<MESSAGES.redo>
我将使用distributive conditional types,如果您有一个类似type D<T> = T extends U ? V : W
的类型,其中选中的类型T
是一个裸类型参数,则条件检查将分布在各个并集上:>
type MessagePayloadDistributive<T extends MESSAGES> = T extends any
? MessagePayload<T>
: never;
type SomeMessagePayload = MessagePayloadDistributive<MESSAGES>;
如果您进行检查,则SomeMessagePayload
会得出上述所需的类型。然后以下代码将按您期望的方式工作:
function pick(payload: SomeMessagePayload): void {
switch (payload.scope) {
case MESSAGES.open:
open(payload.content); // okay
break;
case MESSAGES.close:
close(payload.content); // okay
break;
case MESSAGES.redo:
redo(payload.content); // okay
break;
}
}
好的,希望能有所帮助。祝好运!