- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
考虑具有不同参数的简单函数列表:
const fns = {
isValidDate: (input: string, min?: Date, max?: Date): boolean => {
// ...
return true;
},
isValidOption: (input: string, options: string[]): boolean => {
// ...
return true;
},
};
它们都返回相同的类型(bool);
然后是应该调用上述任何函数的另一个函数:
function validateField(where: string, fn: keyof typeof fns, ...args: any[]){
// ...
return fns[fn](...args);
}
如何使 args
反射(reflect)所选 fn
的参数?
例如:
validateField("test", "isValidDate", new Date()); // should be ok
validateField("test", "isValidDate", 123); // should fail
并在 vscode 提示中显示参数,就像普通函数一样。
我知道我需要为每个 fn
创建 validateField
的重载,但是如何使用类型定义或其他东西来做到这一点......而不必手动定义每个重载并使用这些参数编写重复代码
最佳答案
您可能希望 validateField()
为 generic在 fn
参数的类型中,以便您可以为 args
选择合适的类型。我们可以编写一些辅助实用程序类型来计算:
type Fns = typeof fns;
type FnArgs = { [K in keyof Fns]:
Fns[K] extends (input: string, ...args: infer A) => boolean ? A : never
};
/* type FnArgs = {
isValidDate: [min?: Date | undefined, max?: Date | undefined];
isValidOption: [options: string[]];
} */
FnArgs
类型是 mapped type 其中每个键来自 fns
的类型,每个值都是 tuple初始 string
之后的参数(使用 conditional type inference 获取该列表)。
现在您可以给 validateField()
这个调用签名:
declare function validateField<K extends keyof Fns>(
where: string, fn: K, ...args: FnArgs[K]
): boolean;
当你调用它时它会起作用:
validateField("test", "isValidDate", new Date()); // okay
validateField("test", "isValidDate", 123); // error! number is not assignable to date
validateField("test", "isValidOption", ["a"]) // okay
不幸的是,validateField()
的实现没有类型检查:
function validateField<K extends keyof Fns>(where: string, fn: K, ...args: FnArgs[K]) {
return fns[fn](where, ...args); // error!
// -----------------> ~~~~~~~
// A spread argument must either have a tuple type or
// be passed to a rest parameter.
}
根本问题是缺乏对相关联合 的直接支持,正如microsoft/TypeScript#30581 中所要求的那样.编译器无法理解 fns[fn]
的类型属于与 args
类型相关的函数类型。错误消息有点神秘,但它来自这样一个事实,即它将 args
视为不适合 fns[fn]
参数的元组类型的联合,它被视为没有公共(public)剩余参数类型的函数类型的联合。
幸运的是,microsoft/TypeScript#47109 中描述了一个推荐的解决方案.我们需要给fns
一个新的类型,编译器一眼就能看出是一个对象,其方法的参数与FnArgs
直接相关。这是它的样子:
function validateField<K extends keyof Fns>(where: string, fn: K, ...args: FnArgs[K]) {
const _fns: { [K in keyof Fns]: (str: string, ...args: FnArgs[K]) => boolean } = fns;
return _fns[fn](where, ...args); // okay
}
_fns
变量是 annotated作为映射类型的方法,对于 Fns
的键中的每个 K
显式地带有一个剩余参数 tpe FnArgs[K]
。 fns
到该变量的赋值成功,因为它是同一类型。
但关键的区别在于 _fns[fn](where, ...args)
在 fns[fn](where, ...args)
失败的地方成功.这是因为编译器一直在跟踪 _fns[fn]
类型和 args
类型之间的通用 K
之间的相关性。
现在您拥有了调用者和函数实现所需的东西!
关于javascript - 函数定义的下一个参数取决于第一个参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74467067/
我是一名优秀的程序员,十分优秀!