您需要类似部分类型参数推断之类的功能,这目前不是 TypeScript 的功能(请参阅微软/TypeScript#26242)。现在,您要么必须手动指定所有类型参数,要么让编译器推断所有类型参数;没有片面的推论。正如你所注意到的,泛型类型参数默认值 do not挠痒痒;默认转off推理。
所以有解决方法。那些可以持续工作但使用起来也有些烦人的要么是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
and 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
编译器推断的参数I
and O
, by 注释或以其他方式指定您的内部类型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}
这可能是我在这里推荐的解决方案,实际上与另一个答案已发布。因此,所有这个答案所做的都是煞费苦心地解释为什么你想做的事情目前是不可能的,以及为什么可用的解决方法会导致你远离它。那好吧!
好的,希望这有助于理解这种情况。祝你好运!
Playground 代码链接