Why narrowing matters
A union exposes only the members shared by all its types. To use a member-specific property you must narrow first:
function len(v: string | number): number {
if (typeof v === "string") {
return v.length; // allowed after narrowing
}
return v.toFixed().length; // number path
}
Without the typeof guard, v.length would be a compile error because numbers have no length.
Custom type guards
You can write your own guards with a predicate return type:
function isString(v: unknown): v is string {
return typeof v === "string";
}
function onlyStrings(items: unknown[]) {
return items.filter(isString); // result: string[]
}
The v is string return type tells the compiler to treat the value as a string in the true branch, which is powerful for filtering and validation.