I have the following code that is partly converted to TypeScript (from JavaScript).
Basically if a callback parameter exists, I always want the function itself to return void. Otherwise, return type Promise<object>. With an optional parameter (settings) before that (so technically the callback parameter could be passed in as the settings parameter, which the first few lines of the function handle that use case).
For backwards compatibility purposes (and to keep code DRY), I do not want to create another function called savePromise or saveCallback and separate it out. I'm trying to figure out how to get TypeScript to be smart enough to understand this logic somehow.
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
I'm pretty sure the type file I'm aiming for would be something along the lines of the following. Not sure I'm accurate here tho.
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;
It seems like the callback parameter being passed in as the settings parameter use case can be handled by doing something like:
save(data: string, settings?: object | CallbackType<object, string>, callback?: CallbackType<object, string>): Promise<object> | void
But that is super messy, and from my experience it doesn't seem like TypeScript is smart enough to realize that settings will always be an optional object after those first 4 lines of code in the function. Which means when calling callback you have to type cast it, which again, feels really messy.
How can I achieve this with TypeScript?

