Reputation: 45484
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.
Upvotes: 3
Views: 273
Reputation: 54628
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>>(() => { });
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:
Is there a way to make it safe (no type cast)?
Not necessary
Upvotes: 2
Reputation: 51886
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()
Upvotes: 1