避免在 switch 中进行打字稿转换
Avoid typescript casting inside a switch
考虑以下代码:
interface FooBarTypeMap {
FOO: FooInterface;
BAR: BarInterface;
}
type FooBarTypes = "FOO" | "BAR";
export interface FooBarAction<T extends FooBarTypes> {
type: T;
data: FooBarTypeMap[T];
}
const doSomthingBasedOnType = (action: FooBarAction<FooBarTypes>): void => {
switch (action.type) {
case "FOO":
FooAction((action as FooBarAction<"FOO">));
}
};
const FooAction = (action: FooBarAction<"FOO">): void => {
//do something with action.data
};
现在我想避免在 doSomthingBasedOnType 中看到的强制转换(作为 FooBarAction<"FOO"> 的操作),如果接口使它成为此开关内的唯一可能性,则作为定义。我可以在我的代码中更改某些内容以使其正常工作,还是这只是 TypeScript 中的一个错误?
您需要将FooBarAction
转变为歧视联盟。目前您的 FooBarAction
版本不是很严格,而 type
必须是 "FOO" | "BAR"
之一并且 data
必须是 FooBarTypeMap[FooBarTypes] = FooInterface | BarInterface
之一没有关系两者之间。所以这可以被允许:
let o : FooBarAction2<FooBarTypes> = {
type: "BAR",
data: {} as FooInterface
}
被歧视的联合版本看起来像这样:
export type FooBarAction = {
type: "FOO";
data: FooInterface;
} | {
type: "BAR";
data: BarInterface;
}
const doSomthingBasedOnType = (action: FooBarAction): void => {
switch (action.type) {
case "FOO":
FooAction(action);
}
};
// We use extract to get a specific type from the union
const FooAction = (action: Extract<FooBarAction, { type: "FOO" }>): void => {
//do something with action.data
};
您还可以使用条件类型的分配行为从类型联合创建联合:
interface FooInterface { foo: number}
interface BarInterface { bar: number}
interface FooBarTypeMap {
FOO: FooInterface;
BAR: BarInterface;
}
type FooBarTypes = "FOO" | "BAR";
export type FooBarAction<T extends FooBarTypes> = T extends any ? {
type: T;
data: FooBarTypeMap[T];
}: never;
const doSomthingBasedOnType = (action: FooBarAction<FooBarTypes>): void => {
switch (action.type) {
case "FOO":
FooAction(action);
}
};
const FooAction = (action: FooBarAction<"FOO">): void => {
//do something with action.data
};
考虑以下代码:
interface FooBarTypeMap {
FOO: FooInterface;
BAR: BarInterface;
}
type FooBarTypes = "FOO" | "BAR";
export interface FooBarAction<T extends FooBarTypes> {
type: T;
data: FooBarTypeMap[T];
}
const doSomthingBasedOnType = (action: FooBarAction<FooBarTypes>): void => {
switch (action.type) {
case "FOO":
FooAction((action as FooBarAction<"FOO">));
}
};
const FooAction = (action: FooBarAction<"FOO">): void => {
//do something with action.data
};
现在我想避免在 doSomthingBasedOnType 中看到的强制转换(作为 FooBarAction<"FOO"> 的操作),如果接口使它成为此开关内的唯一可能性,则作为定义。我可以在我的代码中更改某些内容以使其正常工作,还是这只是 TypeScript 中的一个错误?
您需要将FooBarAction
转变为歧视联盟。目前您的 FooBarAction
版本不是很严格,而 type
必须是 "FOO" | "BAR"
之一并且 data
必须是 FooBarTypeMap[FooBarTypes] = FooInterface | BarInterface
之一没有关系两者之间。所以这可以被允许:
let o : FooBarAction2<FooBarTypes> = {
type: "BAR",
data: {} as FooInterface
}
被歧视的联合版本看起来像这样:
export type FooBarAction = {
type: "FOO";
data: FooInterface;
} | {
type: "BAR";
data: BarInterface;
}
const doSomthingBasedOnType = (action: FooBarAction): void => {
switch (action.type) {
case "FOO":
FooAction(action);
}
};
// We use extract to get a specific type from the union
const FooAction = (action: Extract<FooBarAction, { type: "FOO" }>): void => {
//do something with action.data
};
您还可以使用条件类型的分配行为从类型联合创建联合:
interface FooInterface { foo: number}
interface BarInterface { bar: number}
interface FooBarTypeMap {
FOO: FooInterface;
BAR: BarInterface;
}
type FooBarTypes = "FOO" | "BAR";
export type FooBarAction<T extends FooBarTypes> = T extends any ? {
type: T;
data: FooBarTypeMap[T];
}: never;
const doSomthingBasedOnType = (action: FooBarAction<FooBarTypes>): void => {
switch (action.type) {
case "FOO":
FooAction(action);
}
};
const FooAction = (action: FooBarAction<"FOO">): void => {
//do something with action.data
};