TypeScript:如何根据第一个参数省略函数中的第二个参数

时间:2019-09-27 13:42:07

标签: typescript types

根据第一个参数函数返回的内容,我不想在函数中使用第二个参数。我可以将其类型设置为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会显示错误。 TS Error 当我提供参数时会显示错误 enter image description here

1 个答案:

答案 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.fourParams<>类型。


尽管如此,我可能会使用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" });

所有这些都能完美运行。好的,希望对您有所帮助。祝你好运!

Link to code