Learn TypeScript

Implementing generic parameter constraints

Implementing generic parameter constraints

It is possible to require generic parameters to have a particular structure. We'll learn how to do this in this lesson.

Parameter constraint syntax

The syntax for a parameter constraint is to put the type that the parameter must be compatible with after the extends keyword:

<T extends ContrainingType>

A simple example

We are going to explore default generic parameter contraints in an exercise.

TypeScriptOpen exercise in CodeSandbox

The code contains an interface and a generic function.

🤔

The logItems function contains a type error where it references item.log. Why is this so?

  • Add a constraint to the generic parameter in logItems to ensure it has the same structure as the Logable interface.

The type error will now disappear.

Neat!

  • Copy the code from below into the editor:
const heading = {
name: "Heading",
props: { text: "Chapter 1" },
log: () => console.log("Chapter 1 heading"),
};
const button = {
name: "Button",
props: { text: "Save" },
trace: () => console.log("Save button"),
};
logItems([heading, button]);

This code invokes the logItems function with two objects.

🤔

The call to logItems raises a type error. Why is this so?

  • Update the button object to have a log method:
const button = {
...
log: () => console.log("Save button")
};

The type error is resolved.

A more complex example

A parameter constraint can be dependent on another generic parameter. We are going to work through an example of this.

  • Copy the code from below and paste it into the editor:
interface Form<T> {
values: T;
}
function getFieldValue<T>(form: Form<T>, fieldName: string) {
return form.values[fieldName];
}
🤔

The getFieldValue function contains a type error where it references form.values[fieldName]. Why is this so?

  • Update the getFieldValue as follows:
function getFieldValue<T, K extends keyof T>(
...
fieldName: K
) {
...
}

We have added a second generic parameter called K. This has a constraint that requires it to be a property name (or key) from type T.

The TypeScript keyof keyword queries the keys of the type referenced after it.

We have also used K as a type annotation on the fieldName parameter.

The type error on getFieldValue is now resolved.

  • Let's consume the getFieldValue function by copying the code from below and pasting it into the editor:
1const contactForm = {
2 values: {
3 name: "Bob",
4 email: "bob@someemail.com",
5 },
6};
7console.log(getFieldValue(contactForm, "name"));
8console.log(getFieldValue(contactForm, "phone"));
🤔

A type error occurs on line 8. Why is this so?

Good stuff!

Summary

Generic parameter constraints allow us to write more strongly-typed code and are particularly useful in functions and classes. This is because functions and classes contain implementation, and generic parameter constraints will enable us to force generic parameters to have a particular structure for use in the implementation code.

Next up is a quiz to test what we have learned.

Did you find this lesson useful?

Share this lesson on twitter
© 2024 Carl Rippon
Privacy Policy
This site uses cookies. Click here to find out more