Quickly Uderstand TypeScript Generics

Introduction

I’ve always had a fear of TypeScript generics🔗 with all the <T>, <U>, extends, and nested layers. It seems daunting, but generics are essential for specific problems.

Why Do We Need Generics?

Typically, generics are needed when planning reusable code snippets, specifically:

  • A certain type needs conversion (Utility Types🔗)
  • A certain function needs to accept different data types as input…
  • A certain component needs to accept different data types as input… (Vue Generic🔗)

Generics Are Simple

Generics = parameters for types

Compared to assigning specific types to function parameters, which can be too rigid:

function print(message: string): string {
return message
}
print('hi')
print(123) // Argument of type 'number' is not assignable to parameter of type 'string'

Generics enhance the flexibility and reusability of defined types:

function print<MessageType>(message: MessageType): MessageType {
return message
}
print('hi') // function print<"hi">(message: "hi"): "hi"
print(123) // function print<123>(message: 123): 123

One can imagine that the <MessageType> generic type is derived by inferring from message.

extends - Generic Constraints

The extends keyword constrains the possibilities of generics:

function print<MessageType extends string | number>(message: MessageType): MessageType {
return message
}
print('hi')
print(123)
print(false) // Argument of type 'boolean' is not assignable to parameter of type 'string | number'.

This is more like an “adjectives” (X has the properties of string or number) rather than “nouns” (X is string).

Conditional Types

Using syntax similar to the JavaScript Conditional(Ternary) Operator, we can transform different types based on the evaluation of extends:

type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false

Conclusion

Learning generics can help create more versatile types.

Every time I see complex, multi-layered conditional types, I genuinely respect the significant effort required to maintain and manage production-level TypeScript libraries.

Further Reading