I am trying to do the following (also see it on TypeScript playground), but I get an error on the return type of the function that tells me the conditional type can not be assigned to the union:
type RequestType =
| 'foo'
| 'bar'
| 'baz'
interface SomeRequest {
id: string
type: RequestType
sessionId: string
bucket: string
params: Array<any>
}
type ResponseResult = string | number | boolean
async function sendWorkRequest<T extends RequestType>(
type: T,
...params
): Promise<
T extends 'foo'
? string
: T extends 'bar'
? number
: T extends 'baz' ? boolean : never
> {
await this.readyDeferred.promise
const request: SomeRequest = {
id: 'abc',
bucket: 'bucket',
type,
sessionId: 'some session id',
params: [1,'two',3],
}
const p = new Promise<ResponseResult>((/*...*/) => {/*...*/})
this.requests[request.id] = p
this.worker.postMessage(request)
return p // <-------------------------------- ERROR
}
Basically, I want the conditional type to result in one of the types of the ResponseResult type. So based on the type argument passed into the function, it should return one of the types in the ResponseResult union (as a Promise).
How can I make this work, so that the type of the type argument determines the type of Promise returned?
Here's another way to do it without conditional types, but I want to know if it can be done using the conditional type for the type arg.
EDIT: Based on Erik's answer below, I am also curious why this one won't work, and if it is possible to make it work without redefining ResponseResult and without changing the return type of the function.
@Erik, second example.
You need to encapsulate the Type as (assumption) typescript can't assume (calculate) two non-referenced conditional types are the same.
So instead do
type ResponseResult<T> =
T extends 'foo'
? string
: T extends 'bar'
? number
: T extends 'baz' ? boolean : never;
Now you can change the signature of the function to:
async function sendWorkRequest<T extends RequestType>(
type: T,
...params
): Promise<ResponseResult<T>> {
and update p:
const p = new Promise<ResponseResult<T>>(() => { });
TypeScript Playground Example
Do you know why or how to do it without changing the return type and without modifying the definition of the return type?
No because a Conditional Type not equal to a Type.
Does that one require an explicit type cast in the place where the function is called?
No, I can mock the promise with a property of type and see the it is of that type:
TypeScript Playground Example
Is there a way to make it safe (no type cast)?
Not necessary
Alternatively in order to get type inference working you can provide override declarations:
type ResponseResult = string | number | boolean
async function sendWorkRequest(type: 'foo', ...params): Promise<string>
async function sendWorkRequest(type: 'bar', ...params): Promise<number>
async function sendWorkRequest(type: 'baz', ...params): Promise<boolean>
async function sendWorkRequest(type: RequestType, ...params): Promise<ResponseResult> {
/* ... */
const p = new Promise<ResponseResult>((/* ... */) => {/* ... */})
/* ... */
}
// WORKS
async function test1() {
const result = await sendWorkRequest('foo')
result.split('')
}
test1()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With