首页 > 解决方案 > TS 无法使用推断值创建联合类型

问题描述

我正在尝试简化 React 中共享减速器的类型,它目前可以工作,但我想让共享动作调度程序函数知道可以根据动作类型传递给它的类型。

您可以在此处查看代码的简单示例;(以及我尝试过的) TS Playground

function setDada(test: number): void {};
function addDada(): void {};
function setLala(arg: {lala: number, id: number, str: string}): void {};

interface SetDadaArg {
    type: "SET_DADA",
    arg: Parameters<typeof setDada>[0];
}

interface AddDadaArg {
    type: "ADD_DADA",
    arg: Parameters<typeof addDada>;
}

interface SetLaLa {
    type: "SET_LALA",
    arg: Parameters<typeof setLala>[0];
}

type ActionsArg = SetDadaArg | AddDadaArg | SetLaLa;
export type Actions = ActionsArg["type"];

const actions: Record<Actions, any> = {
    SET_DADA: setDada,
    ADD_DADA: addDada,
    SET_LALA: setLala,
};

function dispatch(arg: ActionsArg) {
    actions[arg.type](arg.arg);
}

// Type doesnt match, but should suggest me to use 'number' instead of 'string'
dispatch({type: "SET_DADA", arg: "asd"})

这个想法是定义一次函数(在化简器中,但为了简单起见,在示例之上),然后将参数的类型传递给动作调度程序函数{type, arg}

我可以Parameters<>从接口中删除并在那里分配预期值,它会起作用(调度程序上的建议显示在接口中定义),但是这样做需要我再次键入函数参数,因此如果两者都可能会导致问题键入不同。这是对 TypeScript 的限制,还是有更好的方法来做到这一点?

标签: typescriptreduxparametersunion-types

解决方案


你可以做类似的事情

function setDada(test: number): void { };
function addDada(): void { };
function setLala(arg: { lala: number, id: number, str: string }): void { };

const actions = {
    SET_DADA: setDada,
    ADD_DADA: addDada,
    SET_LALA: setLala,
};
export type Actions = keyof typeof actions;

type DispatchParameter<K extends Actions> = {
    type: K,
    arg: Parameters<typeof actions[K]>[0]
};
function dispatch<K extends Actions>(arg: DispatchParameter<K>) {
    actions[arg.type](arg.arg as any);
}

// Error: Type 'string' is not assignable to type 'number'.ts(2322)
dispatch({ type: "SET_DADA", arg: "abc"});
// OK
dispatch({ type: "SET_DADA", arg: 1});

除了更简洁之外,这还具有可维护性和重构优势。所有类型都通过类型推断进行跟踪。添加新动作或删除动作所要做的就是从actions对象中添加或删除属性。

这种方法的关键是允许类型推断完成它的工作。而不是指定不精确的中间类型,例如Record<Actions, any>.


推荐阅读