Reputation: 477
I met a wrapper function type problem.
I want turn a function with callbacks into a promise function. almost of those functions are like this:
func({
success:....,
fail:....,
complete:...
})
and I wrote a wrapper function:
function toAsync<
OptType
SuccesResType,
FailResType>(fn:(opt:OptType)=>any):(opt:OptType)=>Promise<SuccesResType>{
return async (opt:OptType):Promise<SuccesResType>=>{
return new Promise<SuccesResType>((resolve,reject)=>{
fn({
...(opt || {}),
success(res:SuccesResType){
resolve(res)
},
fail(res:FailResType){
reject(res)
}
} as unknown as OptType)
})
}
}
and there is the problem, basically the SuccessResType
is parameter type of opt.success
,
FailResType
is the parameter type of opt.fail
.
how can I define my toAsync
wrapper to function to get the right type hint when use it like this:
const fn = toAsync(fn)
instead of some duplicated works like
type fnOptType = Parameters<typeof fn>[0]
type fnSuccessRestype = Parameters<fnOptType.SuccessCallback>[0]
type fnFailResType = Parameters<fnOptType.FailCallback>[0]
type fn = toAsync<fnOptType,fnSuccessRestype,fnFailResType>(fn)
Right now, if I use toAsync
like const fn = toAsync(fn)
, I can't get any hint option until I manually write the generic type.
Update:
for fn
, its option type can change.like:
function request(opt:{
fail?:(res:RequestFailResType)=>any;
success?:(res:RequestSuccessResType)=>any;
complete?:(res:RequestCompleteResType)=>any;
url:string
}):any{
// do things here
}
function locationQuery(opt:{
fail?:(res:QueryFailResType)=>any;
success?:(res:QuerySuccessResType)=>any;
complete?:(res:QueryCompleteResType)=>any;
lat:number,
log:number
}):any{
// do things here
}
toAsync
needs to handle those type changes. It seems a big compatible requirement.
I have hundreds of functions like this, therefore I want a more convenient way to do this backwards type support.
Upvotes: 0
Views: 169
Reputation: 2201
The solution is to make OptType
generic. Like this-
interface OptType <T1, T2> {
complete: () => void
success: (successResult: T1) => void
fail: (failResult: T2) => void
}
Then you can pass the argument to toAsync
with generics involved-
fn: (opt: OptType<SuccessResType, FailResType>) => any
This time the types for SuccessResType
and FailResType
will be inferred automatically. The whole solution will be-
declare function myFunc(opts: MyFuncOptType): void
interface OptType <T1, T2, T3> {
complete?: (res: T3) => void
success?: (successResult: T1) => void
fail?: (failResult: T2) => void
}
type SuccessResultType<O extends OptType<any, any, any>> = O extends OptType<infer T1, any, any>
? T1
: never
type FailResultType<O extends OptType<any, any, any>> = O extends OptType<any, infer T2, any>
? T2
: never
declare interface MyFuncOptType extends OptType<number, boolean, any> {
someProp: string
someOtherProp: boolean[]
someFunc: (p: number) => string
}
function toAsync<O extends OptType<any, any, any>>(fn: (opt: O) => any) {
return async (opt: Omit<O, 'success' | 'fail' | 'complete'>) => {
return new Promise<SuccessResultType<O>>((resolve, reject) => {
fn({
...(opt || {}),
success(res: SuccessResultType<O>){
resolve(res)
},
fail(res: FailResultType<O>){
reject(res)
}
} as unknown as O)
})
}
}
const promisedMyFunc = toAsync(myFunc)
promisedMyFunc({
someProp: 'va',
someOtherProp: [false],
someFunc: (p) => p.toString()
}).then((res) => {
console.log(res) // res is number
})
function request(opt:{
fail?: (res: string) => any;
success?: (res: boolean) => any;
complete?: () => any;
url: string
}): any{
// do things here
}
const promisedRequest = toAsync(request)
promisedRequest({
url: '',
}).then((res) => { // res is Blob
console.log(res)
})
function locationQuery(opt:{
fail?:(res: 'type1') => any;
success?:(res: 'type2') => any;
complete?:(res: 'type3') => any;
lat:number,
log:number
}): any {
// do things here
}
const promisedLocationQuery = toAsync(locationQuery)
promisedLocationQuery({
lat: 34,
log: 34
}).then((res) => { // res is "type2"
console.log(res)
})
Please note that I have removed unnecessary types. And rename T1
and T2
to your convenience.
Upvotes: 1