TypeScript Type Narrowing
Type narrowing is a process in TypeScript that allows you to narrow down the type of a variable within a conditional block. This helps TypeScript to understand the specific type of a variable at a given point in the code, enabling better type checking and code completion.
Example of Type Narrowing
Here's an example of type narrowing using the typeof
operator:
function padLeft(value: string | number, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
In this example, the padLeft
function takes two parameters: value
and padding
, which can be either a string or a number.
The typeof
operator is used to narrow down the type of padding
within the conditional blocks.
If padding
is a number, it pads the value
with spaces. If padding
is a string, it concatenates the padding
with the value
.
Other Type Narrowing Techniques
Besides the typeof
operator, TypeScript provides other ways to narrow types:
instanceof
operator: Used to check if an object is an instance of a class.- Type predicates: Custom functions that return a type predicate.
- Discriminated unions: Using a common property to distinguish between different types in a union.
Example using instanceof
:
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else if (animal instanceof Cat) {
animal.meow();
}
}
In this example, the makeSound
function uses the instanceof
operator to narrow down the type of the animal
parameter.
Example using Type Predicates:
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
}
In this example, the isFish
function is a type predicate that narrows down the type of pet
to Fish
if it has a swim
method.
Example using Discriminated Unions:
interface Square {
kind: "square";
size: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Circle;
function getArea(shape: Shape) {
switch (shape.kind) {
case "square":
return shape.size ** 2;
case "circle":
return Math.PI * shape.radius ** 2;
}
}
In this example, the Shape
type is a discriminated union with a common kind
property.
Conclusion
Type narrowing is a powerful feature in TypeScript that enhances type safety and code readability by allowing you to work with more specific types within conditional blocks. By using techniques like typeof, instanceof, type predicates, and discriminated unions, you can write more robust and maintainable code.