I have something I'm trying to do but I'm not quite sure how to achieve it in typescript.
I'm working with discord.js and I'm trying to make a wrapper to convert Discord.User | Discord.PartialUser
(or similar T | PartialT
types) to their non-partial versions.
Something like this:
interface Partial {
partial: boolean;
fetch(): Promise<any>;
}
async function departialize<T extends Partial>(thing: T) {
if(thing.partial) {
return await thing.fetch();
} else {
return thing;
}
}
While this compiles, I would like to include more type information. I.e. with this example I would like it to return Promise<Discord.User>
instead of Promise<Any>
.
Both Discord.User.fetch()
and Discord.PartialUser.fetch()
return Promise<Discord.User>
, can this be deduced in the interface or return type? In C we would write something like decltype(std::declval<T>().fetch())
, not sure what to do in TS.
CodePudding user response:
Here's an overloaded function which will provide a correct return type for your input, narrowing based on the inferred value of the partial
property.
Notes:
Because the
Partial
utility is already declared in TypeScript, I've used the type nameIncomplete
to avoid a compiler error.
unknown
is more type safe thanany
, but feel free to replace if you really needany
.
interface Incomplete<T = unknown> {
partial: boolean;
fetch (): Promise<T>;
}
function departialize <T extends Incomplete & { partial: true }>(thing: T): ReturnType<T['fetch']>;
function departialize <T extends Incomplete & { partial: false }>(thing: T): T;
function departialize <T extends Incomplete>(thing: T): T | ReturnType<T['fetch']>;
function departialize <T extends Incomplete>(thing: T): T | ReturnType<T['fetch']> {
return thing.partial ? thing.fetch() as ReturnType<T['fetch']> : thing;
}
// Usage:
declare const input1: {
partial: boolean;
fetch (): Promise<string>;
someOtherProp: number;
};
const output1 = departialize(input1); // typeof input1 | Promise<string>
declare const input2: {
partial: true;
fetch (): Promise<string>;
someOtherProp: number;
};
const output2 = departialize(input2); // Promise<string>
declare const input3: {
partial: false;
fetch (): Promise<string>;
someOtherProp: number;
};
const output3 = departialize(input3); // typeof input3
CodePudding user response:
Figured out, here's what I'm going with in case it helps anyone else:
type PotentiallyPartial = Discord.AllowedPartial | Discord.Partialize<Discord.AllowedPartial>;
async function departialize<T extends PotentiallyPartial, R extends ReturnType<T["fetch"]>>(thing: T): Promise<R> {
if(thing.partial) {
return thing.fetch();
} else {
return thing as R;
}
};