Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Generics in function definition in TypeScript

Tags:

typescript

I'm trying to make type-safe handlers for redux actions relying on action type. For example, any action could be described as:

type ActionType<T extends string> = {
  type: T
};

For a specific action one can have:

type MyAction = ActionType<'hey' | 'there'>;

Now, I'd like to restrict a handler function to allow only 'hey' or 'there' as a type. Finally I expect something like this:

handleAction<MyAction>('hey');

where the definition of the handler function could be:

function handleAction<A extends ActionType<T>>(type: T){
...
}

But I have a typescript compiler error:


TS2304: Cannot find name 'T'.


So, I have to modify this handler function definition in this way:

function handleAction<A extends ActionType<T>, T extends string>(type: T){
...
}

It works, but looks really ugly:

handleAction<MyAction, 'hey' | 'there'>('hey');

TS Playground

What's a better way to handle this?

like image 298
Warlock Avatar asked Mar 05 '18 01:03

Warlock


People also ask

How do you define a generic type in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.

How TypeScript define generic interface?

TypeScript - Generic Interface The above IProcessor is a generic interface because we used type variable <T> . The IProcessor interface includes the generic field result and the generic method process() that accepts two generic type parameters and returns a generic type. As you learned, you can use interface as type.

What is type T in TypeScript?

This article opts to use the term type variables, coinciding with the official Typescript documentation. T stands for Type, and is commonly used as the first type variable name when defining generics. But in reality T can be replaced with any valid name.

Can you pass a type as a parameter TypeScript?

This is not possible.

How do you create a class type in typescript using generics?

Using Class Types in Generics. When creating factories in TypeScript using generics, it is necessary to refer to class types by their constructor functions. For example, function create < Type > ( c: { new (): Type }): Type {. return new c ();

What is the use of generic functions in JavaScript?

This Generic functions it is really useful if you want to clear your code or receive specific types on your parameters, it helps us when we specified types with interfaces either it looks clean, and it helps a lot to the IDE to kow what r you trying to do with your code. JavaScript With Syntax For Types.

What are generics in programming languages?

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

Why can’t static members use the type parameter of a generic class?

Generic classes are only generic over their instance side rather than their static side, so when working with classes, static members can not use the class’s type parameter.


1 Answers

Edit

You can use a type query to get the type of type:

function handleAction<A extends ActionType<string>>(type: A ['type']) {
}

handleAction<MyAction>('hey');

Or you can use conditional types in 2.8 to extract the generic parameter from ActionType (2.8 is unreleased at the time of writing, but will be released in March 2018, you can get it via npm install -g typescript@next)

type ActionType<T extends string> = {
  type: T
};

type MyAction = ActionType<'hey' | 'there'>;


function handleAction<A extends ActionType<string>>(type: A extends ActionType<infer U> ? U : never) {
}

handleAction<MyAction>('hey');

This solution is based on the inference behavior of conditional type. ActionType<infer U> basically says: If a extends ActionType of some other type U give me the type U. So U will be whatever string literal type was passed to ActionType in our case. Then we use U on the true branch of the conditional type and this becomes the final type of out conditional type. In this case we don't care about the false branch so we use never.

like image 103
Titian Cernicova-Dragomir Avatar answered Sep 18 '22 21:09

Titian Cernicova-Dragomir