What the Heck Are All the <T>s in TypeScript?
If you’re relatively new to TypeScript or typed programming languages in general, the <T> annotation may seem confusing.
They’re called generic type variables (or generics for short) and they’re actually really handy by letting you define a variable within a type. The name T (for type) is just a common naming pattern, but it can really be anything.
Imagine writing a dropdown component that accepts different option types. For example, one page might show a list of languages and another one years. Instead of having to enumerate all the possible value types in the generic component itself, we can declare a generic type that can be freely specified when consuming the component.
Think of it as a function argument that you pass to a type and reference it within the type declaration:
interface Option<T> {
label: string
value: T
}
When we make the dropdown options generic, we can document our code more precisely while keeping the reusable code generic and letting the consumer decide what the implementation details are:
const languageOptions: Option<string> = [
{ label: 'American', value: 'en-US' },
{ label: 'Finnish', value: 'fi-FI' },
{ label: 'Japanese', value: 'ja-JP' },
]
const yearOptions: Option<number> = [
{ label: '1985', value: 1985 },
{ label: '1988', value: 1988 },
]
Generics can be primitives like strings, numbers, and booleans, or entire interfaces!
Here’s how you’d use generics in a function:
interface Struct<T> {
value: T
}
function createStruct<T>(value: T): Struct<T> {
return {
value,
}
}
Then you can use this function by explicitly specifying a type for T:
const withNumber = createStruct<number>(200)
const withString = createStruct<string>('hello')
Or you can let the TypeScript compiler automatically infer the type based on what arguments you feed it:
const withNumber = createStruct(200)
const withString = createStruct('hello')