TypeScript 中的命令和响应泛型

Command and response generics in TypeScript

我正在努力为 Command -> Response 泛型想出一个好的结构。

我的目标是让函数根据类型或接口接受命令列表中的命令,并使用该接口推断响应类型。

到目前为止,我已经设法根据 type 属性 正确推断出预期的 data,但我似乎无法全神贯注地推断出 return 通用类型。

非常欢迎任何指向类似示例的指针!我没能找到多少

代码示例: TypeScript Playground

type MappedData<E, T extends keyof E> = E[T];
type MappedType<E> = {
    [K in keyof E]: {
        type: K;
        data: MappedData<E, K>;
    };
}[keyof E];

interface ServerCommandInterface<T> {
     _res?: T;
}

interface TestCommandA extends ServerCommandInterface<{responseA: boolean}> {
    param_a: string;
}

interface TestCommandB extends ServerCommandInterface<{responseB: boolean}> {
    param_b: string;
}

interface Commands {
    command_a: TestCommandA;
    command_b: TestCommandB;
}

function execute<C extends MappedType<Commands>>(command: C): typeof command.data._res {
    // Logic (HTTP call or similar) that would result in _res type
    return null as any;
}

const result = execute({
    type: 'command_a',
    data: {
        param_a: 'param',
    }
});

console.log(result.responseA); // I expect this to be a boolean

所以我认为您正在寻找的是类似于以下助手类型的东西:

interface Commands {
    command_a: {
        param: { param_a: string },
        response: { responseA: boolean }
    },
    command_b: {
        param: { param_b: string },
        response: { responseB: boolean }
    }
}

这里 Commands 的每个 属性 都有一个键对应于传递给 execute() 的值的 type 属性。该值是具有两个属性的对象类型:paramresponseparam属性对应传给execute()的值的data属性,而response属性对应值 return 来自 execute()。请注意,名称 paramresponse 是完全任意的,我们可以随意命名。这里不会涉及 Commands 类型的任何值;它只是一个辅助类型,允许我们以一种简单的方式表达 execute() 的调用签名:

function execute<K extends keyof Commands>(
  command: { type: K, data: Commands[K]['param'] }
): Commands[K]['response'] {
    return null as any;
}

所以execute()函数是generic in the type parameter K, constrained to be one of the keys of Commands. Then the parameter to execute() has a type property of type K, and a data property of type Commands[K]['param']. (We're using indexed access types获取CommandsK键控属性的类型,然后得到"param"-keyed 属性)。 return 类型是 Commands[K]['response'].

让我们看看它是否有效:

const result = execute({
    type: 'command_a',
    data: {
        param_a: 'param',
    }
});

此处编译器推断 "command_a"K,因此调用签名指定为

/* function execute<"command_a">(command: {
    type: "command_a";
    data: {
        param_a: string;
    };
}): {
    responseA: boolean;
} */

因此 result 是类型

/* const result: {
    responseA: boolean;
} */

符合预期:

result.responseA === true // okay

Playground link to code