gpt4 book ai didi

typescript :在可选的第一个泛型之后推断泛型的类型

转载 作者:行者123 更新时间:2023-12-04 12:08:21 25 4
gpt4 key购买 nike

我有一个具有两种泛型类型的函数,InOut :

function createTask<
In extends Record<string, any> = {},
Out extends Record<string, any>,
>(task : TaskFunction<In, Out>) : Task<In, Out>

type TaskFunction<In, Out> = (args : TaskWrapper<In>) => Out | Promise<Out>;
// TaskWrapper wraps several other types and interfaces, so args is more than just `In`

此代码当前无法编译,因为在可选类型 ( Out ) 之后不能有必需的泛型类型 ( In )。

我如何告诉 Typescript 编译器我想让这个函数的用户做三件事之一:
  • 不要指定任何泛型:createTask(...) . In的类型应该默认为 {} , 和 Out应该从 TaskFunction 的返回值推断出来.
  • 仅指定 In :createTask<A>(...) .如上,Out应该推断。
  • 指定两个 InOut :createTask<A, B>(...) .

  • 本质上,我正在寻找一种方法来说明“这个泛型是可选的,应该被推断出来”。我知道有一个 infer关键字,但从我在其上找到的有限文档来看,它似乎不支持此用例。

    我还尝试为 Out 分配一个默认值,但它总是使用该默认值而不是从 TaskFunction 推断.

    我可以颠倒 In的顺序和 Out ,然后 Out如果用户想指定 In,即使很容易推断,也必须始终指定。 .

    我也不想强制用户添加默认值 {}每次他们调用该函数时。

    这是否与 Typescript 有关,还是我必须始终需要 In要指定?

    最佳答案

    您需要诸如部分类型参数推断之类的东西,它目前不是 TypeScript 的功能(请参阅 microsoft/TypeScript#26242 )。现在,您要么必须手动指定所有类型参数,要么让编译器推断所有类型参数;没有部分推断。如您所见,generic type parameter defaults不要抓这个痒;默认值关闭推理。

    所以有解决方法。那些一直工作但使用起来也有些烦人的是 currying或“假装”。在这里柯里化(Currying)意味着您将单个多类型参数函数拆分为多个单类型参数函数:

    type Obj = Record<string, any>; // save keystrokes later

    declare const createTaskCurry:
    <I extends Obj = {}>() => <O extends Obj>(t: TaskFunction<I, O>) => Task<I, O>;

    createTaskCurry()(a => ({ foo: "" }));
    // I is {}, O is {foo: string}
    createTaskCurry<{ bar: number }>()(a => ({ foo: "" }));
    // I is {bar: number}, O is {foo: string}
    createTaskCurry<{ bar: number }>()<{ foo: string, baz?: number }>(a => ({ foo: "" }));
    // I is {bar: number}, O is {foo: string, baz?: number}

    对于您的 I,您有您想要的确切行为。和 O类型,但是有这个烦人的延迟函数调用。

    这里的虚拟意味着你给函数一个你想要手动指定的类型的虚拟参数,并让推理代替手动指定:
    declare const createTaskDummy:
    <O extends Obj, I extends Obj = {}>(t: TaskFunction<I, O & {}>,
    i?: I, o?: O) => Task<I, O>;

    createTaskDummy(a => ({ foo: "" }));
    // I is {}, O is {foo: string}
    createTaskDummy(a => ({ foo: "" }), null! as { bar: number });
    // I is {bar: number}, O is {foo: string}
    createTaskDummy(a => ({ foo: "" }), null! as { bar: number },
    null! as { foo: string, baz?: number });
    // I is {bar: number}, O is {foo: string, baz?: number}

    同样,您拥有所需的行为,但您将无意义/虚拟值传递给函数。

    当然,如果您已经拥有正确类型的参数,则不需要添加“虚拟”参数。在您的情况下,您当然可以在 task 中提供足够的信息。编译器推断的参数 IO , 来自 annotating或以其他方式指定 task 中的类型范围:
    declare const createTaskAnnotate: 
    <O extends Obj, I extends Obj = {}>(t: TaskFunction<I, O>) => Task<I, O>;

    createTaskAnnotate(a => ({ foo: "" }));
    // I is {}, O is {foo: string}
    createTaskAnnotate((a: { bar: number }) => ({ foo: "" }));
    // I is {bar: number}, O is {foo: string}
    createTaskAnnotate((a: { bar: number }): { foo: string, baz?: number } => ({ foo: "" }));
    // I is {bar: number}, O is {foo: string, baz?: number}

    这可能是我在这里推荐的解决方案,实际上与 the other answer posted 相同。 .因此,所有这些答案都在煞费苦心地解释为什么您想要做的事情目前不可能,以及为什么可用的解决方法使您远离它。那好吧!

    好的,希望这有助于理解情况。祝你好运!

    Playground link to code

    关于 typescript :在可选的第一个泛型之后推断泛型的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60377365/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com