Using typeof
type guards for non-array types is pretty straightforward (example from the docs):
function padLeft(value: string, 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}'.`); }
However, when it comes to array types it gets more complicated. I would assume that the following code would work:
function join(array: number[] | string[]): string {
if (typeof array[0] === 'number') {
return foo(array);
} else {
return bar(array)
}
}
function foo(n: number[]): string {
return n.join();
}
function bar(s: string[]): string {
return s.join();
}
Seems quite simple to me: the expected type is either array of numbers or array of strings.
If the type of the first element in the array is number
then the array is of type number[]
. Otherwise, this is a strings array.
Unfortunately, TypeScript compiler is not that smart and I get a compilation error:
Argument of type 'number[] | string[]' is not assignable to parameter of type 'string[]'
How do I make it work?
A type guard is a TypeScript technique used to get information about the type of a variable, usually within a conditional block. Type guards are regular functions that return a boolean, taking a type and telling TypeScript if it can be narrowed down to something more specific.
In Typescript, Type aliases give a type a new name. They are similar to interfaces in that they can be used to name primitives and any other kinds that you'd have to define by hand otherwise. Aliasing doesn't truly create a new type; instead, it gives that type a new name.
In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.
To check if a value is an array of specific type in TypeScript: Use the Array. isArray() method to check if the value is an array.
The answer is User-Defined Type Guards.
You can define a type guard of your own that will determine whether the array is array of numbers or not:
function isNumbers(array: number[] | string[]): array is number[] {
return typeof array[0] === 'number'
}
Read more about user defined type guards here.
The following is a working example of your code:
function join(array: number[] | string[]): string {
if (isNumbers(array)) {
return foo(array);
} else {
return bar(array)
}
}
function foo(n: number[]): string {
return n.join();
}
function bar(s: string[]): string {
return s.join();
}
function isNumbers(array: number[] | string[]): array is number[] {
return typeof array[0] === 'number'
}
TypeScript playground example.
JeB's answer was a very good start for me but I needed something a little more robust because I have to deal with arbitrary input. Here's what I came up with:
function isNumberArray(value : unknown) : value is number[] {
if (!Array.isArray(value)) {
return false;
}
if (value.some((v) => typeof v !== "number")) {
return false;
}
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With