DEV Community

Cover image for How Typescript Generics Work
David
David

Posted on • Updated on • Originally published at easywebdev.substack.com

How Typescript Generics Work

The following article is a free sample from EasyWebDev mailing list. Subscribe to get simple web development guides, tutorials, and explanations.

If you've been using Typescript, there is a good chance that you've come across Generics. Generics are a handy Typescript feature that allows you to declare variable types for your interfaces, types, and declarations.

interface GenericInterface<T> {
    prop: T;
}
Enter fullscreen mode Exit fullscreen mode

T is a Generic that allows the consumer to specify the type for our property prop. This is handy in situations where all the possible types are not known ahead of time or where there are a wide variety of types that we don't need to constrain on the interface level.

Using Generics

Using generics in your application code is conceptually very similar to passing arguments to to your functions. However, instead of passing run time values, we are passing Typescript type definitions.

const myObject: GenericInterface<string> = {
    prop: 'string'
}
Enter fullscreen mode Exit fullscreen mode

Generics make our code more flexible but they still also guarantee type safety.

const myObject: GenericInterface<string> = {
    prop: 1, // Error! string is passed to the generic, but a number is specified.
}
Enter fullscreen mode Exit fullscreen mode

Non-Trivial Types

Complex Types

The values passed to a Generic do not need to be simple types. Any valid Typescript expression is allowed. The important take-away is that the concrete value must adhere to the Generics' type, whatever it is.

const unionObject: GenericInterface<string | number> = {
    prop: 1; // this can be either a string or a number
}

const typeofObject: GenericInterface<typeof window.document> = {
    prop: window.document; // this can only be an object that matches the type from window.document
}
Enter fullscreen mode Exit fullscreen mode

Multiple Generics

There is no limit to the number of generics that you can define for a given interface, type, or declaration.

interface GenericInterface<A, B, B> {
    prop: A;
    next: B;
    another: C;
}
Enter fullscreen mode Exit fullscreen mode

Default Types

When a generic is defined, the caller always has to specify a type for the generic. To get around this requirement, Generics can be assigned default types. When a default type is used and a type has not been specified, Typescript will use the default Generic type.

interface GenericInterface<A = string> {
    prop: A;
}

const myObject: GenericInterface = {
    prop: 'string'
}
Enter fullscreen mode Exit fullscreen mode

Default types allow you to do some very complex and interesting inference. Here is a silly example:

interface GenericInterface<A extends { prop: string | number }, B = A['foo']> {
    a: A;
    b: B
}

const obj: GenericInterface<{ prop: string }> = {
    a: {
        prop: 'string',
    },
    b: 1 // Error! b MUST match the type from a.prop.
}
Enter fullscreen mode Exit fullscreen mode

Constraining Generics

By default there are no constraints on the types that can be passed to a Generic. Sometimes, however, we want to constrain a Generic's type. One way we can do this is aliasing the constraint to a new type. This applies a localized constraint and does not globally constraint the Generic's type.

type Constrained = GenericInterface<string>;
function myfunction(arg: Constrained) {

}
Enter fullscreen mode Exit fullscreen mode

Another way to constraing Generics is on the Generic's definition itself. This constraint will apply to any usage of the Generic, so all callers will need to adhere to the constraint when using extends directly in the Generic's definition.

interface GenericInterface<T extends string> {
    prop: T
}
Enter fullscreen mode Exit fullscreen mode

Naming Generics

This is a somewhat controversial topic, but there is a long-standing convention to use single letter names for your Generics. I would encourage you to use whatever approach works best for your team and application.

Happy coding!

Enjoy this article? Subscribe to EasyWebDev!

Top comments (0)