bilalq
bilalq

Reputation: 7679

Defining a type alias using nested generic parameters

I'd like to be able to define a type that takes in a generic parameter (that itself extends a type with 2 generic parameters) and define its constraints by using the nested generic parameters.

// Boiler plate for example setup:
type NodeCallback<T> = (err?: Error | void, result?: T) => void
type Handler<I, R> = (event: I, callback: NodeCallback<R>) => void
type StrToNumHandler = Handler<string, number>

// I end up having to do this:
type WorkaroundAsyncHandler<T extends Handler<I, R>, I, R> = (event: I) => Promise<R>
type WorkaroundStrToNumHandler = WorkaroundAsyncHandler<StrToNumHandler, string, number>

// I'd like to be able to just write this:
type AsyncHandler<T extends Handler<I, R>> = (event: I) => Promise<R> // This is a compiler error. Not sure how to write this in a valid way.
type AsyncStrToNumHandler = AsyncHandler<StrToNumHandler> // This is the line I'd ultimately like to write

The workaround I have compiles, but if I'm supplying the values for I and R, there isn't any value in supplying the Handler, which I'd like to be able to do.

Upvotes: 4

Views: 601

Answers (1)

artem
artem

Reputation: 51559

You can get pretty close with conditional types and type inference in conditional types

type AsyncHandler<T> = T extends Handler<infer I, infer R> ? 
                             (event: I) => Promise<R> 
                          :  never
;

type AsyncStrToNumHandler = AsyncHandler<StrToNumHandler> 
// inferred as AsyncStrToNumHandler = (event: string) => Promise<number>

The downside is that your type alias should provide some type even if T is not actually Handler. The usual solution is to return never type. But the code that uses such alias can behave unexpectedly at compile time when it receives never type.

Upvotes: 1

Related Questions