Generic Primitive 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.

Generic Primitive Inference

Subscription Required

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

Concept Introduction

function arrayFree<T>(a: T, b: T) {
return [a, b];
}

const free = arrayFree(1, 2);
// ^?

function arrayConstrained<T extends number>(a: T, b: T) {
return [a, b];
}

const constrained = arrayConstrained(1, 2);
// ^?

const explicit = arrayConstrained<number>(1, 2);
// ^?

String Real World Example

function createSwatchMeh<C>(colors: C[]) {
return colors.concat();
}

const meh = createSwatchMeh(['red', 'blue', 'purple']);
// ^?

function createSwatchBetter<C extends string>(colors: C[]) {
return colors.concat();
}

const better = createSwatchBetter(['red', 'blue', 'purple']);
// ^?
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

There are various features we can use to guide the TypeScript inference engine for generics. One impressive way that is officially supported by the compiler is by using a generic constraint, which is specified with the extens operator. Let's consider a simple identity generic function that is not currently spec any constraints. It takes a value of type T and then returns the value of type T. Now, if you invoke this function with the number, as you would expect, you would get back a number and the type inferred for T is going to be number. But something interesting happens when we add a constraint to the function that T extends number.

00:36

Now, when we invoke the function with the same argument, which is the number 47, we actually get back the literal type 47 instead of the open-ended number. And this is an official feature of the TypeScript compiler. If you specify a constraint for a primitive type and pass in a literal of that type, the inferred type will be that literal. So in this particular case, since we are passing in the literal 47, the inferred type is the literal 47. Now, of course, there is nothing stopping you from being explicit, so we can specify

01:09

that the genetic type should be number, and with that, the result will be of type number. This narrowing of primitives to the little values occurs with other JavaScript primitive types as well. Let's take a look at another example with the string type. We create a simple swatch function that takes a generic occupancy, takes a colors array consisting of C values, and then returns a copy of the array. If you create a swatch consisting of colors, red, blue, and purple, what do you think is going to be the result? As you would expect, because C is unconstrained, it is going to be the common type between these, which is a string.

01:44

Now, wouldn't it be better if you could see which values would exist within this array, and we can actually achieve that with a generic constraint? The only thing that we have modified over here is that we have added the constraint extends string, and now when we invoke the function with the same values, red, blue, and purple TypeScript will narrow it down a bit further and we get the littles red, blue, and purple in the output array.