【问题标题】:Typecheck createAction Redux helper类型检查 createAction Redux 助手
【发布时间】:2018-01-09 21:56:04
【问题描述】:

我有这样定义的Action 类型:

type Action =
    { type: 'DO_X' }
    |
    { type: 'DO_Y', payload: string }
    |
    { type: 'DO_Z', payload: number }

这是一个联合类型,其中每个成员都是有效的操作。

现在我想创建一个接受 type 的函数 createAction 并返回一个接受 payload 的新函数。

const doZ = createAction('DO_Z')
console.log(doZ(42)) // { type: 'DO_Z', payload: 42 }

这是我当前的实现:

const createAction = (type: Action['type']) =>
  (payload?: any) =>
    ({ type, payload })

它会像我想要的那样对type 进行类型检查。我如何也可以检查payload?我希望payload 匹配基于type 的正确操作类型。例如,doZ 在使用 string 调用时应该会失败,因为 payload 表示它只接受 number

【问题讨论】:

  • 你试过像<Type, Payload>(type: Type) : (payload: Payload) => {type: Type, payload: Payload}这样的定义吗?
  • @EmrysMyrooin 这似乎不起作用。类型被用作值。但仿制药似乎是朝着正确方向迈出的一步。 TypeScript 怎么知道TypePayload 来自Action
  • 你在打字稿上...我还没有检查标签。我其实不知道,我是用 Flow 打字的!

标签: typescript redux union flux


【解决方案1】:

冗长但可行:

type XAction = { type: 'DO_X', payload: undefined }; 
type YAction = { type: 'DO_Y', payload: string }; 
type ZAction = { type: 'DO_Z', payload: number }; 

type Action = XAction | YAction | ZAction;

const createAction = <T extends Action>(type: T['type']) =>
    (payload: T['payload']) =>
        ({ type, payload });

// Do compile:

createAction<XAction>("DO_X")(undefined);
createAction<YAction>("DO_Y")("foo");
createAction<ZAction>("DO_Z")(5);

// Don't compile:

createAction<XAction>("DO_X")(5); // Expected `undefined`, got number
createAction<YAction>("DO_Y")(5); // Expected string, got number
createAction<ZAction>("DO_X")(5); // Expected `"DO_Z"`, got `"DO_X"`

更简单的方法(不强制createAction的类型参数):

type Action = { type: 'DO_X', payload: undefined } | { type: 'DO_Y', payload: string } | { type: 'DO_Z', payload: number };

createAction("DO_Y")("foo");

不幸的是,允许createAction&lt;YAction&gt;("DO_Y")(5) 等编译,因为T 总是被推断为Action,因此payload 参数是string|number|undefined

【讨论】:

    【解决方案2】:

    此问题的规范答案取决于您的具体用例。我将假设您需要 Action 来准确评估您编写的类型;也就是说,type: "DO_X" 的对象 not 具有任何类型的 payload 属性。这意味着createAction("DO_X") 应该是零参数的函数,而createAction("DO_Y") 应该是单个string 参数的函数。我还将假设您希望自动推断 createAction() 上的任何类型参数,这样您就不需要为 Blah 的任何值指定 createAction&lt;Blah&gt;("DO_Z")。如果解除这些限制中的任何一个,您可以将解决方案简化为@Arnavion 给出的解决方案。


    TypeScript 不喜欢从属性 values 映射类型,但很高兴从属性 keys 映射类型。因此,让我们以一种为我们提供编译器可以用来帮助我们的类型的方式构建Action 类型。首先,我们像这样描述每种操作类型的有效负载:

    type ActionPayloads = {
      DO_Y: string;
      DO_Z: number;
    }
    

    让我们也介绍任何没有有效负载的 Action 类型:

    type PayloadlessActionTypes = "DO_X" | "DO_W";
    

    (我添加了一个'DO_W' 类型只是为了展示它是如何工作的,但你可以删除它)。

    现在我们终于可以表达Action

    type ActionMap = {[K in keyof ActionPayloads]: { type: K; payload: ActionPayloads[K] }} & {[K in PayloadlessActionTypes]: { type: K }};
    type Action = ActionMap[keyof ActionMap];
    

    ActionMap 类型是一个对象,其键是每个 Actiontype,其值是 Action 联合的对应元素。它是Actions 与payloads 和Actionpayloads 的交集。而Action 只是ActionMap 的值类型。验证 Action 是否符合您的预期。

    我们可以使用ActionMap 来帮助我们输入createAction() 函数。这里是:

    function createAction<T extends PayloadlessActionTypes>(type: T): () => ActionMap[T];
    function createAction<T extends keyof ActionPayloads>(type: T): (payload: ActionPayloads[T]) => ActionMap[T];
    function createAction(type: string) {
      return (payload?: any) => (typeof payload === 'undefined' ? { type } : { type, payload });
    }
    

    这是一个重载函数,类型参数T 对应于您正在创建的Actiontype。前两个声明描述了这两种情况:如果TActiontype 而没有payload,则返回类型是返回正确类型Action 的零参数函数。否则,它是一个单参数函数,它采用正确的payload 类型并返回正确的Action 类型。实现(第三个签名和正文)与您的类似,只是如果没有传入payload,它不会将payload 添加到结果中。


    全部完成!我们可以看到它按预期工作:

    var x = createAction("DO_X")(); // x: { type: "DO_X"; }
    var y = createAction("DO_Y")("foo"); // y: { type: "DO_Y"; payload: string; }
    var z = createAction("DO_Z")(5); // z: { type: "DO_Z"; payload: number; }
    
    createAction("DO_X")('foo'); // too many arguments
    createAction("DO_X")(undefined); // still too many arguments
    createAction("DO_Y")(5); // 5 is not a string
    createAction("DO_Z")(); // too few arguments
    createAction("DO_Z")(5, 5); // too many arguments
    

    您可以在on the TypeScript Playground 看到这一切。希望对你有效。祝你好运!

    【讨论】:

      猜你喜欢
      • 2021-10-24
      • 1970-01-01
      • 1970-01-01
      • 2011-11-24
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 2014-11-27
      相关资源
      最近更新 更多