typescript - 如何使用可选参数重载 TypeScript 中的函数?
问题描述
我有以下代码部分转换为 TypeScript(来自 JavaScript)。
基本上,如果callback
存在参数,我总是希望函数本身返回void
。否则,返回类型Promise<object>
。在此之前有一个可选参数 ( settings
)(因此从技术上讲,callback
参数可以作为settings
参数传入,函数的前几行处理该用例)。
出于向后兼容的目的(并保持代码干燥),我不想创建另一个名为savePromise
or的函数saveCallback
并将其分开。我试图弄清楚如何让 TypeScript 足够聪明,以某种方式理解这个逻辑。
type CallbackType<T, E> = (response: T | null, error?: E) => void;
class User {
save(data: string, settings?: object, callback?: CallbackType<object, string>): Promise<object> | void {
if (typeof settings === "function") {
callback = settings;
settings = undefined;
}
if (callback) {
setTimeout(() => {
callback({"id": 1, "settings": settings});
}, 1000);
} else {
return new Promise((resolve) => {
setTimeout(() => {
resolve({"id": 1, "settings": settings});
}, 1000);
});
}
}
}
const a = new User().save("Hello World"); // Should be type Promise<object>, should eventually resolve to {"id": 1, "settings": undefined}
const b = new User().save("Hello World", (obj) => {
console.log(obj); // {"id": 1, "settings": undefined}
}); // Should be type void
const c = new User().save("Hello World", {"log": true}); // Should be type Promise<object>, should eventually resolve to {"id": 1, "settings": {"log": true}}
const d = new User().save("Hello World", {"log": true}, (obj) => {
console.log(obj); // {"id": 1, "settings": {"log": true}}
}); // Should be type void
我很确定我想要的类型文件将类似于以下内容。不确定我在这里是否准确。
save(data: string, settings?: object): Promise<object>;
save(data: string, callback: CallbackType<object, string>): void;
save(data: string, settings: object, callback: CallbackType<object, string>): void;
似乎可以通过执行以下操作来处理作为参数用例callback
传入的参数:settings
save(data: string, settings?: object | CallbackType<object, string>, callback?: CallbackType<object, string>): Promise<object> | void
但这太混乱了,根据我的经验,TypeScript 似乎不够聪明,无法意识到settings
在函数的前 4 行代码之后它始终是一个可选对象。这意味着在调用时callback
您必须键入 cast 它,这再次让人感觉非常混乱。
如何使用 TypeScript 实现这一目标?
解决方案
TL;博士
这是解决方案(进行了一些重构):
type CallbackType<T, E> = (response: T | null, error?: E) => void;
interface ISettings {
log?: boolean;
}
interface ISaveResult {
id: number;
settings: ISettings | undefined;
}
class User {
save(data: string): Promise<ISaveResult>;
save(data: string, settings: ISettings): Promise<ISaveResult>;
save(data: string, callback: CallbackType<ISaveResult, string>): void;
save(data: string, settings: ISettings, callback: CallbackType<ISaveResult, string>): void;
save(data: string, settings?: ISettings | CallbackType<ISaveResult, string>, callback?: CallbackType<ISaveResult, string>): Promise<ISaveResult> | void {
if (typeof settings !== "object" && typeof settings !== "undefined") {
callback = settings;
settings = undefined;
}
const localSettings = settings; // required for closure compatibility
if (callback) {
const localCallback = callback; // required for closure compatibility
setTimeout(() => {
localCallback({ id: 1, "settings": localSettings });
}, 1000);
} else {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, "settings": localSettings });
}, 1000);
});
}
}
}
const a = new User().save("Hello World"); // User.save(data: string): Promise<ISaveResult>
const b = new User().save("Hello World", obj => {
console.log(obj); // obj: ISaveResult | null
}); // User.save(data: string, callback: CallbackType<ISaveResult, string>): void
const c = new User().save("Hello World", { "log": true }); // User.save(data: string, settings: ISettings): Promise<ISaveResult>
const d = new User().save("Hello World", { "log": true }, (obj) => {
console.log(obj); // obj: ISaveResult | null
}); // User.save(data: string, settings: ISettings, callback: CallbackType<ISaveResult, string>): void
见码盘
解释
aFunction
也是 an Object
,因此 TypeScript 无法隐式区分两者。通过创建特定的ISettings
接口,您可以让 TypeScript 区分设置对象和回调函数。
最简单的查看方法是查看 TypeScript 输出的错误以及代码流进行时的变量类型,例如(您的代码):
推荐阅读
- node.js - Nuxt 构建不包含 node_modules CSS
- vue.js - 如何将引导 vue 中的导航栏固定在中心?
- java - Java:了解 Comparator 的工作原理(返回 -1 和 0 )
- html - 动画 feDropShadow 内部过滤器
- javascript - React - 更改未使用的道具是否会重新渲染组件?
- amazon-ecs - ECS CLI efsVolumeConfiguration Docker Compose / ECS 参数 yml
- kubernetes - 如何修改 K8s pod 描述符而不重新启动它?
- git-pull - 执行合并命令期间捕获的异常
- node.js - 如何在 NodeJS 应用程序中显示当前用户?
- android - 活动结果 API 未返回 RequestPermission Contract 的回调