DEV Community

Cover image for Typescript generic type - basic concept
Lucas jin for One Beyond

Posted on

Typescript generic type - basic concept

If it's your first time working with a Typescript project, the generic type can be intimidating. It's already complicated dealing with all other normal types, the generic type makes it look like we are working with another different language rather than Javascript.

How to declare a type with a defined type value

To talk about the generic type, let's see an example of how to declare a type with a defined type value in Typescript:

type A = number[]

interface AB {
  a: A
  b: string
}
Enter fullscreen mode Exit fullscreen mode

We have declared a type A, which is an array of numbers, and an interface B, which has properties a and b. It's quite simple and clear, right?

But now if we want another type, which will be an array of strings and another interface that will contain this type, what should we do? Of course, we can simply create another type

type C = string[]

interface CD {
  c: C 
  d: string
}
Enter fullscreen mode Exit fullscreen mode

How to declare a generic type

But don't you remember one of the principles of software development is "Don't repeat yourself"? In this case Typescript generic type comes to the rescue. The purpose of the generic type is to create reusable types, does it sound familiar, you may have heard that the functions are used to create reusable code as well. It turns out they are quite similar. Let's see how to create a generic type:

type A<T> = T[]

interface AB<T> {
  a: A<T>
  b: string
}
Enter fullscreen mode Exit fullscreen mode

You see, the difference between the normal type and the generic type is that the generic type has an angle bracket. And also when we declare a type like this type A = number[] we know the type is an array of numbers, but we don't know what type is yet when we declare the generic type. That's why it's called generic type, because it can be any type.

As I said before, the generic type and the function are very similar. You can think this way: here we declared a type A, and we pass a parameter T to it, then we return a type of array of T. So when we use the generic type, we have to pass an argument to it:

type A<T> = T[]

const a:A<number> = [1, 2, 3] // Correct.

const a:A<string> = ['1', '2', '3'] // Correct.

const a:A<number | string> = ['1','2', 3] //Correct

const a:A<number> = ['1','2','3'] //Error. Type 'string' is not assignable to type 'number'
Enter fullscreen mode Exit fullscreen mode
const ab:AB<number> = {
  a:[1, 2, 3], // Correct
  b:'b'
}

const ab:AB<string> = {
  a:['1', '2', '3'], // Correct
  b:'b'
}

const ab:AB<number> = {
  a:['1','2','3'], // Error. Type 'string' is not assignable to type 'number'.
  b:'b'
}
Enter fullscreen mode Exit fullscreen mode

In this way, we can create any type we want, as long as they share the same structure. You can often see that people use T, U, and K as the generic type parameter, but you really can name it whatever you want, maybe a semantic word is a good idea.

Declare generic type with default type

Like functions, we also can pass a default type to the generic type, in case we don't pass any type to it, it will be set to the default type.

type A<T = string> = T[]

interface AB<T = number> {
  a: A<T>
  b: string
}


const a: A = ['1','2','3'] // Correct

const a: A = [ 1, 2, 3] // Error. Type 'number' is not assignable to type 'string'.

const ab: AB = {
  a:[ 1, 2, 3], // Correct
  b:'b'
}

const ab: AB = {
  a:[ '1', '2', '3'], // Error. Type 'string' is not assignable to type 'number'.
  b:'b'
}
Enter fullscreen mode Exit fullscreen mode

Use more than one parameter

In the above examples, we see how to create a generic type with a single parameter, but the truth is you can use as many parameters as you want, just like functions.

interface AB<T,U> {
  a: T[]
  b: U
}

const ab:AB<number, string> = {
  a: [1,2,3],
  b: '123'
} // Correct

const ab:AB<number, string> = {
  a: ['1','2','3'], // Error. Type 'string' is not assignable to type 'number'.
  b: 123 // Error. Type 'number' is not assignable to type 'string'.
} 

Enter fullscreen mode Exit fullscreen mode

Also, you can combine the default type with several parameters, but there is one condition that the optional type parameters can't be added before the required parameters:

interface AB<T = number, U = string> { // Correct.
  a: T[]
  b: U
}

interface AB<T, U = string> { // Correct.
  a: T[]
  b: U
}

interface AB<T = number ,U> { // Error. Required type parameters may not follow optional type parameters.
  a: T[]
  b: U
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)