Learn TypeScript

Creating generic interfaces

Creating generic interfaces

In this lesson, we learn how to create generic interfaces.

Generic interface syntax

We can pass types into an interface that is used within its definition like we can to a function. The syntax for a generic interface is below:

interface InterfaceName<T1, T2, ...> {
...
}

The members of the interface can reference the generic types passed into it.

Generic interface example

A common use case for a generic interface is a generic form interface. This is because all forms have values, validation errors, etc. but the specific fields and validation rules differ from form to form. Let's build a simple generic form interface in an exercise.

TypeScriptOpen exercise in CodeSandbox

The code contains a simple form interface where the type for the form values is a generic parameter.

  • Add the interface below for the fields on a contact form:
interface Contact {
name: string;
email: string;
}
  • Add a contactForm object variable to store the actual values on the contact form:
const contactForm = {
values: {
name: "Bob",
email: "bob@someemail.com",
},
};
  • Use our two interfaces to create a strongly-typed version of the contactForm variable.

We will now expand the Form interface to include a property for the form's validation errors. This will be based on the generic type passed into Form, but not all the fields will have validation errors.

  • Let's add this errors property to our Form interface:
interface Form<T> {
errors: {
[K in keyof T]?: string;
};
values: T;
}

The errors property is given a type that has syntax we haven't covered so far in this course. So let's break this down:

  1. The type is in curly brackets, so we are constructing an object type.
  2. [K in keyof T] will put all the keys in the type T into a string literal union. This will be "name" | "email" for contactForm.
  3. [K in keyof T] is the property name of the object being constructed. So, for contactForm, the object's properties are name and email.
  4. The ? after the property name means the properties are optional.
  5. The type for the properties is string.
  6. So, for contactForm, the type for the errors is {name?: string; email?: string}.
🤔

Notice that a type error is now raised on contactForm. Why is this?

  • Add an empty errors object to our contactForm object:

The type error will now disappear.

  • Add an error for the email to our contactForm object:
const contactForm: Form<Contact> = {
errors: {
email: "This must be a valid email address"
},
values: { ... }
};
🤔

What if we add an error for a field that doesn't belong to our contactForm object. Will a type error be raised? Let's try this:

const contactForm: Form<Contact> = {
errors: {
age: "You must enter your age"
},
values: { ... }
};

That completes our simple form generic type.

Summary

Using generic interfaces allows generic object types to be created that we can make specific by supplying our types as parameters.

Is it possible to use generics in type aliases? We'll find out in the next lesson.

© 2023 Carl Rippon
Privacy Policy
This site uses cookies. Click here to find out more