Currently I'm designing a library, and there is my own type Result<T, E>, which is a union of Ok<T> and Error<E> variants. This type will be used frequently in the codebase so for convenience I'm going to write a utility that delegates the Error<E> variant to outer scope, like ? operator in Rust. At the first I designed the utility like this:
const wrap = <T>(fn: () => T): Result<T, unknown> => {
try {
const res = fn()
return somehowWrapOk(res)
} catch (err) {
return somehowWrapError(err)
}
}
const req/*uire*/: <T>(x: unknown, r: Result<T, any>) => asserts x is T = (x, r) => {
if (/* the Result is Error */) throw // the error
}
// simplified
declare const x: unknown
const _: Result<number, unknown> = wrap(() => {
req(x, 0 as any as Result<number, unknown>)
return x // number
})
```This works, but it loses type information about `E` :\/ Is there a way to keep the type information while keeping conciseness?
\* And I realized this "widening" behavior may help me:
```ts
const arr = [] // arr: any[]
arr.push(0)
arr.push('')
arr // arr: (string | number)[]
```and it actually worked:
```ts
const err = []
err[0] = /*T*/ extractErrorType(someResult1) // someResult1: Result<any, T>
err[0] = /*U*/ extractErrorType(someResult2) // someResult2: Result<any, U>
err // (T | U)[]
```but I feel this is ugly 😕
Maybe I should just discard type information about `E` variant?