Type narrowing vs type widening
type type Narrowing = "Alves"Narrowing = "Alves";
type type Widening = stringWidening = string;Tired of hearing the termstype narrowingandtype wideningwithout really knowing what they mean? In this article you'll understand these two important concepts and see practical examples of each.
Type narrowing
To understand both concepts, let's start with this scenario: imagine someone told you"I'm going to give you a car as a gift".
Notice that "a car"could be any car. You don't know the color, the brand, the model, whether it's electric, or even whether it's a real car or a toy.
Now consider this sentence:"I'm going to give you a remote-control car, a red Ferrari" .
Now the set of possibilities is much smaller. You know a lot more about this car.
Turning broad information into more specific information: that'snarrowing.
Type widening
Type widening is exactly the opposite of type narrowing.
Now imagine this:"The car I gave you broke, but I'll give you another one" .
Before, we had a red remote-control Ferrari. Now, once again, we don't know what car it is. Will it be the same Ferrari? Will it still be a Ferrari? Will it still be red? Will it still be remote-controlled?
Turning specific information into broader information: that's widening.
Narrowing and widening in practice
Take this example: a function that receives a parameter that can be either a string or a number.
function function doSomething(value: string | number): anydoSomething(value: string | numbervalue: string | number) {
return value: string | numbervalue.toUpperCase();}If we called string methods on a number, we'd get a runtime error. What we need here is to shrink the set of possibilities and only call a string method when the parameter really is a string, which makes the code safe. We need to dotype narrowing.
function function doSomething(value: string | number): stringdoSomething(value: string | numbervalue: string | number) {
if (typeof value: string | numbervalue === "string") {
return value: stringvalue.String.toUpperCase(): stringConverts all the alphabetic characters in a string to uppercase.toUpperCase();
}
return "Couldn't convert to uppercase";
}Now let's understand type widening. Look at this other example: we declare a variable with the value "7" using the const keyword. Something interesting happens: TypeScript inferred that the type of this variable is exactly "7".
const const value: "7"value = "7"; // type inferred as "7"If you think about it, it makes sense, right? Variables declared with const can't be reassigned. That value will never change, so TypeScript could infer "7".
But when we declare the variable with let, TypeScript doesn't infer the type as exactly "7". It infers string instead. And again, that makes sense, right? A variable declared with let can be reassigned.
let let value: stringvalue = "7"; // type inferred as stringBefore we had something more specific, now we have something broader. What used to be only the string "7" can now be any string. TypeScript didtype widening.
Type inference for variables declared with "let"
Maybe you're wondering: but couldn't it be anything? Instead of string, shouldn't the type be any? Or unknown? I can reassign it to anything, right?
Yes, you're right, but in this case TypeScript is trying to be your friend. The type will be as broad as possible, but based on the variable's initial value.
If you really want it to be anything, and to be able to reassign your variable that used to hold a string to a number, for example, you need to declare the possible types explicitly.
let let value: string | numbervalue: string | number = "7";
let value: string | numbervalue = 7;And that's the end of this article. I hope it's now clear what each term means and how it applies in TypeScript. Share this article if it helped you, see you next time:)
Playground with the examples