One aspect of control flow based type analysis is that the TypeScript compiler narrows the type of a variable within a type guard.
This lesson explores how you can define functions and type predicates to create your own type guards similar to the Array.isArray()
method.
I don't understand the syntax here:
function flatten(array: (number | number[])[]):number[] {
@o-t-w: The array
parameter is typed to be an array whose elements must be numbers or number arrays. You could also write the types like this:
function flatten(array: Array<number | Array<number>>): Array<number> {
// ...
}
How would you implement the opposite to isFlat function? Call it "containsArrays" for instance
@Arseniy: You could implement an isArrayOfArrays
function like this:
function isArrayOfArrays(array: unknown[]): array is unknown[][] {
return array.every(element => Array.isArray(element));
}
Hi @marius,
I am not sure about the aim of the keyword is , it's not really well documented inside TS type predicate doc, they spoke about runtime check ... could you explain the aime here ? Regards
@Alexandre: A type guard lets TypeScript narrow the type of a variable. For example, if you have a variable x
of type string | number
, you can check its type using the typeof
operator:
if (typeof x === "string") {
// In here, `x` has type `string`
}
TypeScript has a built-in understanding for type guards using e.g. the typeof
or instanceof
operators. However, in some cases you might need a more complex check. That's when you'd use a custom type guard function.
A custom type guard must return a boolean, but its return type isn't simply boolean
— it has the shape paramName is someType
where paramName
is the name of a parameter of that function and someType
is an arbitrary TypeScript type. You can implement the function in any way you like, as long as it returns a boolean.
When you use your custom type guard function, TypeScript will then narrow the type of the variable that paramName
in paramName is someType
to someType
. For example:
function isArrayOfArrays(array: unknown[]): array is unknown[][] {
return array.every(element => Array.isArray(element));
}
I would of liked a more involved usage and meaning of the "is" usage. Like, why not just do: Why the need for array is T[], rather than just T[]? I have the same lack of understanding for the "as" usage too..
function isFlat<T>(array: (T | T[])[]): T[] {
console.log(!array.some(Array.isArray));
}
function isFlat<T>(array: (T | T[]): array is T[] {
return !array.some(Array.isArray);
}
Wouldn't this throw an error if array
was T as opposed to T[]?
@Jessica: Have a closer look at the exact function signature:
function isFlat<T>(array: (T | T[])[]): array is T[] {
// ...
}
The array
parameter is of type (T | T[])[]
, not (T | T[])
. This means it's always an array.