Define Custom Type Guard Functions in TypeScript

Marius Schulz
InstructorMarius Schulz

Share this video with your friends

Send Tweet

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.

o-t-w
o-t-w
~ 2 years ago

I don't understand the syntax here:

function flatten(array: (number | number[])[]):number[] {
Marius Schulz
Marius Schulzinstructor
~ 2 years ago

@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> {
  // ...
}
Arsenii Shelestiuk
Arsenii Shelestiuk
~ 2 years ago

How would you implement the opposite to isFlat function? Call it "containsArrays" for instance

Marius Schulz
Marius Schulzinstructor
~ 2 years ago

@Arseniy: You could implement an isArrayOfArrays function like this:

function isArrayOfArrays(array: unknown[]): array is unknown[][] {
  return array.every(element => Array.isArray(element));
}
Joël
Joël
~ 2 years ago

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

Marius Schulz
Marius Schulzinstructor
~ 2 years ago

@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));
}
Dean
Dean
~ 2 years ago

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));
}
Jessica
Jessica
~ 9 months ago
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[]?

Marius Schulz
Marius Schulzinstructor
~ 9 months ago

@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.