Typescript 使用适当的参数类型承诺所有满意的功能
Typescript promisfy all satisfied functions with proper param type
我正在开发一个包含大量异步 api 的代码库,在它们的参数中涉及 success
选项,例如
declare function foo(_: {
success?: (_: string) => void,
fail?: () => void,
}): void
declare function bar(_: {
success?: (_: string) => void,
fail?: () => void,
src?: string
}): void
declare function baz(_: {
success?: (_: string) => void,
fail?: () => void,
expired: number
}): void
declare function foobar(_: {
success?: (_: string) => void,
fail?: () => void,
token: string,
state: boolean
}): void
我想用下面的代码承诺所有这些
interface Cont<R> {
fail?: () => void
success?: (_: R) => void
}
interface Suspendable<O> {
(option: O): void
}
function suspend<R, O extends Cont<R>>(fn: Suspendable<O>) {
return async (opt: Omit<Omit<O, "success">, "fail">) => await new Promise<R>((resolve, _) => fn({
...opt,
success: it => resolve(it),
fail: ()=> resolve(undefined)
} as O )) // if any chance, I'd like to omit the `as O` but forgive it for now
}
(async () => {
let _foo = await suspend(foo)({}) // good
let _bar = await suspend(bar)({}) // good
let _baz = await suspend(baz)/* compile error here */ ({ expired: 100})
})()
我是否错过了打字稿中的一些好东西来帮助我捕获 fn
参数中 O
的真实类型,以便我可以很好地约束参数并传递编译器错误?
我想到了两种方法去这里。第一个是放弃拥有两个类型参数,只推断 O
并从中计算 R
:
function suspend<O extends Cont<any>>(fn: Suspendable<O>) {
return async (opt: Omit<O, "success" | "fail">) =>
await new Promise<Parameters<Exclude<O["success"], undefined>>[0]>(
(resolve, _) => fn({
...opt,
success: it => resolve(it),
fail: () => resolve(undefined)
} as O));
}
R
的计算看起来很不幸 Parameters<Exclude<O["success"], undefined>>[0]
。现在以下编译没有错误:
(async () => {
let _foo = await suspend(foo)({})
let _bar = await suspend(bar)({})
let _baz = await suspend(baz)({ expired: 2 })
})()
如果您不断言 as O
,您得到的关于 O
的错误是一个好消息; fn()
的参数可能具有 success
或 fail
比 Cont<any>
:
中指定的更窄的属性
declare function hmm(opt: {
success?: (_: unknown) => void,
fail?: () => string // narrower than specified in Cont<any>
}): void;
suspend(hmm);
并且由于您调用 fn()
时使用的 arg 具有手动 success
和 fail
方法,因此您不能保证它的行为与 fn()
相同期望。所以我认为你需要断言并继续前进。
另一种方法是保留两个类型参数,并依靠 success
和 fail
是可选方法这一事实来使其工作:
function suspend2<O, R>(fn: Suspendable<O & Cont<R>>) {
return async (opt: O) =>
await new Promise<R>(
(resolve, _) => fn({
...opt,
success: (it: R) => resolve(it),
fail: () => resolve(undefined)
}));
}
Here we are inferring both `O` and `R` from an argument `fn` of type `Suspendable<O & Cont<R>>`. Ideally this would automatically yield the right `R` as well as an `O` object type that doesn't include `success` or `fail` methods. But what happens is that you'll get the right `R` (yay) but the value of `O` will be the full object type including the `success` and `fail` methods (boo).
至少现在你不需要像 as O
这样的类型断言,因为你传递的是 O & Cont<R>
.
类型的值
以下仍然可以正常工作:
(async () => {
let _foo = await suspend2(foo)({})
let _bar = await suspend2(bar)({})
let _baz = await suspend2(baz)({ expired: 2 })
})()
但这只是因为 O
不需要 fail
或 success
属性。
无论如何,希望其中之一对您有所帮助;祝你好运!
我正在开发一个包含大量异步 api 的代码库,在它们的参数中涉及 success
选项,例如
declare function foo(_: {
success?: (_: string) => void,
fail?: () => void,
}): void
declare function bar(_: {
success?: (_: string) => void,
fail?: () => void,
src?: string
}): void
declare function baz(_: {
success?: (_: string) => void,
fail?: () => void,
expired: number
}): void
declare function foobar(_: {
success?: (_: string) => void,
fail?: () => void,
token: string,
state: boolean
}): void
我想用下面的代码承诺所有这些
interface Cont<R> {
fail?: () => void
success?: (_: R) => void
}
interface Suspendable<O> {
(option: O): void
}
function suspend<R, O extends Cont<R>>(fn: Suspendable<O>) {
return async (opt: Omit<Omit<O, "success">, "fail">) => await new Promise<R>((resolve, _) => fn({
...opt,
success: it => resolve(it),
fail: ()=> resolve(undefined)
} as O )) // if any chance, I'd like to omit the `as O` but forgive it for now
}
(async () => {
let _foo = await suspend(foo)({}) // good
let _bar = await suspend(bar)({}) // good
let _baz = await suspend(baz)/* compile error here */ ({ expired: 100})
})()
我是否错过了打字稿中的一些好东西来帮助我捕获 fn
参数中 O
的真实类型,以便我可以很好地约束参数并传递编译器错误?
我想到了两种方法去这里。第一个是放弃拥有两个类型参数,只推断 O
并从中计算 R
:
function suspend<O extends Cont<any>>(fn: Suspendable<O>) {
return async (opt: Omit<O, "success" | "fail">) =>
await new Promise<Parameters<Exclude<O["success"], undefined>>[0]>(
(resolve, _) => fn({
...opt,
success: it => resolve(it),
fail: () => resolve(undefined)
} as O));
}
R
的计算看起来很不幸 Parameters<Exclude<O["success"], undefined>>[0]
。现在以下编译没有错误:
(async () => {
let _foo = await suspend(foo)({})
let _bar = await suspend(bar)({})
let _baz = await suspend(baz)({ expired: 2 })
})()
如果您不断言 as O
,您得到的关于 O
的错误是一个好消息; fn()
的参数可能具有 success
或 fail
比 Cont<any>
:
declare function hmm(opt: {
success?: (_: unknown) => void,
fail?: () => string // narrower than specified in Cont<any>
}): void;
suspend(hmm);
并且由于您调用 fn()
时使用的 arg 具有手动 success
和 fail
方法,因此您不能保证它的行为与 fn()
相同期望。所以我认为你需要断言并继续前进。
另一种方法是保留两个类型参数,并依靠 success
和 fail
是可选方法这一事实来使其工作:
function suspend2<O, R>(fn: Suspendable<O & Cont<R>>) {
return async (opt: O) =>
await new Promise<R>(
(resolve, _) => fn({
...opt,
success: (it: R) => resolve(it),
fail: () => resolve(undefined)
}));
}
Here we are inferring both `O` and `R` from an argument `fn` of type `Suspendable<O & Cont<R>>`. Ideally this would automatically yield the right `R` as well as an `O` object type that doesn't include `success` or `fail` methods. But what happens is that you'll get the right `R` (yay) but the value of `O` will be the full object type including the `success` and `fail` methods (boo).
至少现在你不需要像 as O
这样的类型断言,因为你传递的是 O & Cont<R>
.
以下仍然可以正常工作:
(async () => {
let _foo = await suspend2(foo)({})
let _bar = await suspend2(bar)({})
let _baz = await suspend2(baz)({ expired: 2 })
})()
但这只是因为 O
不需要 fail
或 success
属性。
无论如何,希望其中之一对您有所帮助;祝你好运!