How to properly filter values (types) using a function?
Starting with obvious example:
function normalizeSize(size: string | number): string {
if (typeof size === "number") {
// TypeScript can easily understand that the [size] here is value of type [number]
return `${size}px`;
}
// And here TypeScript will tell you that the [size] is a string;
return size;
}
This will cover most of your scenarious, but there is a chance that you will need to move your checks into the function:
function isNumber(value: unknown): boolean {
return typeof value === "number";
}
function normalizeSize(size: string | number): string {
if (isNumber(size)) {
return `${size}px`;
}
return size; // TypeScript error: Type 'string | number' is not assignable to type 'string'.
}
Now, TypeScript cannot guess what is the type of size in branches
of your code as it doesn't have enough power to dig into the logic of the function
and so it doesn't know the relation between its arguments and the result value.
The solution is to describe the relation between the argument and the result
in the type of the function signature. We can do it by replacing boolean with some TypeScript math:
function isNumber(value: unknown): value is string {
return typeof value === "number";
}
While TypeScript doesn't dig into function body it does parse its signature
type and when it will see value is string it will link the type
of the function return to the value argument.
While the example with string and number isn't impressive,
the demonstrated trick will be useful in a real-world projects:
function isPremiumUser(user: User): user is PremiumUser { /* ... */ }