For a function I'd like to provide a data source in a generic way, e.g. either as url string
or a callback that returns a Promise<string>
const processUrl = async (url: string | (() => Promise<string>)) => {
//Retrieve url...
const urlData = typeof url === "function" ? await url() : url;
//Proceed...
};
This works, however I have problems generalizing this approach using a generic
type DataInput<T> = T | (() => Promise<T>);
const retrieveDataInput_Impl1 = <T, DataInputType extends DataInput<T>>(t: DataInputType): Promise<T> => typeof t === "function" ? t() : t;
//No error, but return type is always unknown
const retrieveDataInput_Impl2 = <T extends any>(t: DataInput<T>): Promise<T> => typeof t === "function" ? t() : t;
//Shows the following error for t(): This expression is not callable. Not all constituents of type '(() => Promise<T>) | (T & Function)' are callable. Type 'T & Function' has no call signatures.ts(2349)
const b1 = await retrieveDataInput_Impl1("a"); // type of b1 is unknown, getDataInput1 is showing no error
const b2 = await retrieveDataInput_Impl2("a"); // type of b2 is string, but getDataInput2 is showing an error
How could this functionality be implemented with full type safety?
Disallowing T
to be a function fixes the ambiguity. You should also make the function async
so it still returns a Promise when the argument is returned directly.
type DataInput<T> = T extends Function ? never : (() => Promise<T>) | T;
const retrieveDataInput_Impl3 = async <T>(t: DataInput<T>): Promise<T> => typeof t === "function" ? t() : t;
retrieveDataInput_Impl3('foo') // => Promise<"foo">
retrieveDataInput_Impl3({ key: 'value' }) // => Promise<{ key: string }>
retrieveDataInput_Impl3(async () => 123) // => Promise<number>
retrieveDataInput_Impl3(async () => {}) // => Promise<void>
retrieveDataInput_Impl3(() => {}) // => Error
This solution doesn't seem to accept T
of type boolean
though, because of some other issue.
Since it doesn't matter whether an async function returns a value or a Promise of the value, you might as well allow passing any function and fix your error with a simple type assertion.
type DataInput<T> = (() => Promise<T>) | (() => T) | T;
const retrieveDataInput_Impl3 = async <T,>(t: DataInput<T>): Promise<T> =>
typeof t === "function" ? (t as Function)() : t;
retrieveDataInput_Impl3(() => 42) // => Promise<number)>
retrieveDataInput_Impl3(async () => Promise.resolve(42)) // => Promise<number>
retrieveDataInput_Impl3(() => () => 42) // => Promise<() => number)>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments