【问题标题】:Avoid typescript casting inside a switch避免在开关内进行打字稿转换
【发布时间】:2019-04-22 20:49:08
【问题描述】:

考虑以下代码:

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 的操作),如果接口使它成为此开关内的唯一可能性,则作为定义。我可以在我的代码中更改一些内容以使其正常工作,还是这只是 TypeScript 中的一个错误?

【问题讨论】:

    标签: typescript casting typescript-typings typescript-generics


    【解决方案1】:

    您需要将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
    };
    

    【讨论】:

    • 似乎工作正常,但你能解释一下这部分:T extends any 吗? {类型:T;数据:FooBarTypeMap[T]; }:从不;
    • @MrMamen 这被称为“分布式条件类型”,您可以在此处阅读更多信息:typescriptlang.org/docs/handbook/advanced-types.html
    • 我已经扩展了接口以包含它们自己的类型。使用这个我希望我可以生成一个(通用)FooBarAction,但我正在努力打字。 interface FooInterface { foo: number, type: "FOO"} interface BarInterface { bar: number, type: "BAR"} const createFooBarAction = (fooBarData: FooBarTypeMap[FooBarTypes]): FooBarAction&lt;FooBarTypes&gt; =&gt; ({ type: fooBarData.type, data: fooBarData })
    • @MrMamen 您能否将其作为新问题发布,并附上完整代码?我现在无法回答,也许其他人会回答,而 cmets 是放置大量代码的一个非常糟糕的地方:)
    • 好点,在这里创建了一个:stackoverflow.com/questions/53428841/…
    猜你喜欢
    • 2018-08-27
    • 2017-06-23
    • 2014-02-19
    • 2020-06-01
    • 2019-07-06
    • 2020-04-09
    • 1970-01-01
    • 2017-01-16
    • 2022-01-23
    相关资源
    最近更新 更多