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);
};
当然,你会失去参数名称。如果你想保留它们,你得写一个脚本来生成重载签名。
