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.
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 ourForm
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:
- The type is in curly brackets, so we are constructing an object type.
[K in keyof T]
will put all the keys in the typeT
into a string literal union. This will be"name" | "email"
forcontactForm
.[K in keyof T]
is the property name of the object being constructed. So, forcontactForm
, the object's properties arename
andemail
.- The
?
after the property name means the properties are optional. - The type for the properties is
string
. - 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.