网站搜索

typescript - 是否可以对规范化的参数进行类型检查?


假设我有以下函数签名:

function someFunction(arg1?: number, arg2?: string, arg3?: string|string[]) {}

每个参数都是可选的,所以它应该被调用为

someFunction(undefined, undefined, [''])

如果我想省略其中的一些。

现在,使用像 Node-NormalizeArguments 这样的库,我可以以以下方式封装这个函数:

function wrappedFunction() {
  const normalizedArgs = args(arguments, [
    args.number(undefined), 
    args.string(undefined),
    args.oneOf([
      args.array,
      args.string
    ], undefined)
  ]);
  return someFunction(...normalizedArgs)
}

从而允许将其调用为

someFunction([''])

得到相同的结果。

当然已经有一些缺点(我通过使用 args.array 失去了 string[] 类型检查),但这些可能在库代码中得到修复。真正的问题是:

我如何定义这个新封装函数的类型?(最好不要冗长地列出所有可能的参数组合,或者至少不要手动列出)

答案#1

你可以使用一堆类型别名来生成包含所有参数子集的一组重载签名:

// `CAaOb`: first `a` parameters are already assigned, remaining `b` are
// optional.  `X` is the union of the types of the optional parameters following
// the last assigned parameter that are assumed to have already been skipped,
// meaning that the runtime type of the next argument must not belong to `X`.

type CA0O0<X, R> = () => R;

type CA1O0<A1, X, R> = (a1: A1) => R;
type CA0O1<X, O1, R> = CA1O0<Exclude<O1, X>, never, R> & CA0O0<X | O1, R>;

type CA2O0<A1, A2, X, R> = (a1: A1, a2: A2) => R;
type CA1O1<A1, X, O1, R> = CA2O0<A1, Exclude<O1, X>, never, R> & CA1O0<A1, X | O1, R>;
type CA0O2<X, O1, O2, R> = CA1O1<Exclude<O1, X>, never, O2, R> & CA0O1<X | O1, O2, R>;

type CA3O0<A1, A2, A3, X, R> = (a1: A1, a2: A2, a3: A3) => R;
type CA2O1<A1, A2, X, O1, R> = CA3O0<A1, A2, Exclude<O1, X>, never, R> & CA2O0<A1, A2, X | O1, R>;
type CA1O2<A1, X, O1, O2, R> = CA2O1<A1, Exclude<O1, X>, never, O2, R> & CA1O1<A1, X | O1, O2, R>;
type CA0O3<X, O1, O2, O3, R> = CA1O2<Exclude<O1, X>, never, O2, O3, R> & CA0O2<X | O1, O2, O3, R>;

function someFunction(arg1?: number, arg2?: string, arg3?: string|string[]) { }

const wrappedFunction: CA0O3<never, number, string, string | string[], void> = function() { 
  const normalizedArgs = args(arguments, [
    args.number(undefined), 
    args.string(undefined),
    args.oneOf([
      args.array,
      args.string
    ], undefined)
  ]);
  return someFunction(...normalizedArgs);
};

当然,你会失去参数名称。如果你想保留它们,你得写一个脚本来生成重载签名。