TypeScript NoInfer Utility and Generic Inference

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

TypeScript NoInfer Utility and Generic Inference

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

NoInfer Basics

function nice<T>(value: NoInfer<T>) {
return value;
}

const explicit: string = nice<string>('hello'); // βœ…
// function nice<string>(value: string): string

const implicit: string = nice('hello'); // πŸ›‘ Error: `unknown` is not assignable to `string`
// function nice<unknown>(value: unknown): unknown

Example Challenge

Can you think of a way to get a TypeScript compiler error if the defaultColor is not one of the values present in colors?

function createStreetLight<C extends string>(colors: C[], defaultColor?: C) {
// ...
}

createStreetLight(['red', 'yellow', 'green'], 'red');
// createStreetLight<"red" | "yellow" | "green">

createStreetLight(['red', 'yellow', 'green'], 'blue'); // No Error 😱
// createStreetLight<"red" | "yellow" | "green" | "blue">

Solution

Use NoInfer to exclude defaultColor from the list of candidates for type C.

function createStreetLight<C extends string>(colors: C[], defaultColor?: NoInfer<C>) {
// ...
}

createStreetLight(['red', 'yellow', 'green'], 'red');
// createStreetLight<"red" | "yellow" | "green">

createStreetLight(['red', 'yellow', 'green'], 'blue'); // Error 😎
// createStreetLight<"red" | "yellow" | "green">
javascript
typescript
react
playwright

Enjoy free content straight from your inbox πŸ’Œ

No spam, unsubscribe at any time.

Transcript

00:00

TypeScript comes with a really cool built-in intrinsic utility called No Infer. You can use it to explicitly opt out of generic inference for an input parameter. Let's explain with a few examples. First up here is the definition for the no infer utility. You can see that it takes a single generic argument. However, for the body of the type, we only have the word intrinsic. What this means is that the type is baked into the brain of the TypeScript compiler, and it's not something that we could create ourselves by using other features

00:31

provided by TypeScript in order to appreciate it. Let's do a quick recap of some basic generic inference. We have a simple generic function that takes a value of type T. Now, of course, we can explicitly provide the generic argument when we use the function. For example, here we are saying that T should be a string, and that is what TypeScript will use to instantiate the function, but we can actually leave it to TypeScript to infer it from the provided JavaScript argument. Since here, we are providing the literal Hello TypeScript will automatically infer T

01:03

to be the literal hello without us having to specify it explicitly. Now let's look at how the situation changes. If you were to use the no infer utility to mark the input argument so that it is excluded from any type inference, of course we can still explicitly provide a type four T. For example. Here we are providing the type string, and that is exactly what TypeScript we'll use to instantiate all instances of T. However, if you do not provide the genetic argument, the only way that TypeScript could potentially in further value right now would be from the input argument.

01:35

However, the parameter for that argument has been marked as no infer. So TypeScript will not use it for inference and instead resolve to the fallback value, which is unknown. And over here we would actually get an error. That unknown is not assignable to string. You can see that it is a pretty straightforward utility in terms of its functionality. The real power comes when you learn to apply it effectively. So let's take a look at an example that demonstrates a practical use case and sheds light into how the TypeScript generic inference works. Internally consider a simple generic function called create

02:08

streetlight that takes a generic argument, C, which must be of type string, as we've discussed previously. Using extends string helps TypeScript infer the literal values as the type for the argument. The function takes two parameters, colors, which is going to be an array of C, and a default color, which is going to be C. So technically both of these parameters can be used as a part of the generic inference. Now, if we create a streetlight consisting of red, yellow, and green colors and provide a default value of red, the final result for the generic type would be

02:41

red, yellow, and green. And this is perfectly fine because we have our three colors for the streetlight, which is red, yellow, and green. And the default color red is from that colors list. However, there is a potential of making a mistake. For example, here we've created a streetlight consisting of colors, red, yellow, and green. But for the default color, we have provided the value blue. Now instead of complaining type strip is going to silently add blue to the list of candidates for type C. So the instantiated type four C would consist of a red, yellow, green, and blue.

03:13

Can you think of a type strip way to enforce that the default color C should come from the colors array? The answer to this challenge is to replace the type for default color from C to be no in for C. And now the arguments to default color do not play into the inference for type C. The argument blue no longer makes it into the candidate list for C, and therefore we get an error if you try to provide a default color that does not exist within the colors array.