根据第一个参数函数返回的内容,我不想在函数中使用第二个参数。我可以将其类型设置为undefined
,但仍然需要传递第二个参数。 Sandbox
declare class Params<T> {
private params: T;
}
interface Data {
one: string;
two: {
three: string;
four: Params<{ more: string }>;
};
}
type MyFunction = <T>(
g: (d: Data) => T,
args: T extends Params<infer Args> ? Args : undefined
) => T extends Params<{}> ? string : T extends string ? T : unknown;
let mf = (() => {}) as MyFunction;
let a = mf(p => p.one); // expect not to have second arg
var b = mf(p => p.two); // b is unknown (correct now)
var b = mf(p => p.two.four,); // second arg has "more" property
还有一个问题,在示例b
中,我有一个工具提示,我可以传递第二个参数,但是当我这样做时,TS会显示错误。
当我提供参数时会显示错误
答案 0 :(得分:1)
如果您希望函数根据通用类型采用一个或两个参数,则可以使用rest parameters as tuples来表示。像这样:
type MyFunction = <T>(
g: (d: Data) => T,
...args: T extends Params<infer Args> ? [Args] : []
) => T extends Params<{}> ? string : T extends string ? T : unknown;
这与您所拥有的类似,只是现在args
是一个rest参数,根据T
的不同,它的长度可以是一个或零个元素。现在可行:
let mf = (() => {}) as MyFunction;
let a = mf(p => p.one);
var b = mf(p => p.two);
var c = mf((p: Data) => p.two.four, { more: "ads" });
但是请注意,对于c
,我必须将p
明确注释为Data
。没有它,编译器会感到困惑并推断出any
,然后{more: "ads"}
就是一个错误(因为它无法告诉p.two.four
是Params<>
类型。>
尽管如此,我可能会使用overloads来区分两种完全不同的调用此函数的方式:
type MyFunction = {
<A>(g: (d: Data) => Params<A>, args: A): string;
<T>(g: (d: Data) => T): T extends string ? T : unknown;
};
这要少得多的类型操作(并切掉了大多数条件类型),现在编译器确实知道会发生什么:
let mf = ((() => {}) as any) as MyFunction;
let a = mf(p => p.one);
var b = mf(p => p.two);
var c = mf(p => p.two.four, { more: "ads" });
所有这些都能完美运行。好的,希望对您有所帮助。祝你好运!