Testing static types in TypeScript

Testing static types in TypeScript

In this article, we’ll examine how we can test static types in TypeScript (inferred types, etc.)

Warning: This is experimental work. Read the comments for more information on its limitations.

For example, given the following function:

function createPoint(x: number, y: number) {
  return {x, y};
}

We’d like to check in a unit test that TypeScript infers this return type:

{x: number, y: number} 

In order to do that, we need a few tools that we are going to look at first.

1 - Conditional types

A conditional type lets you switch between two types, depending on whether a given type fulfills a type assertion:

«Type» extends «TypeAssertion» ? «ThenType» : «ElseType» 

In this case, “fulfilling” means: Is Type a subtype of TypeAssertion? If you think of types as sets then “subtype of” means “subset of”.

At the moment (due to limitations of TypeScript type inference), conditional types are mainly useful for computing with types. And not, e.g., to type the results of functions.

*1.1 - A simple example *

In the following example, we store the result of computing with types in a type Result:

type Result = RegExp extends Object ? true : false;
  // type Result = true;

Note that true and false are types here (so-called literal types).

1.2 - Parameterized types as functions for types

In the following example, we use the parameterized type YesNo like a function for types. T is a type parameter.

type YesNo<T extends boolean> = T extends true ? 'yes' : 'no';

type yes = YesNo<true>;
  // type yes = 'yes';
type no = YesNo<false>;
  // type no = 'no';

2 - Asserting types via conditional types

We can use conditional types to test static types. For example, via the following parameterized type:

type AssertIsString<S> = S extends string ? true : never; 

This type has a parameter S whose value is a static type. If that type is a subtype of string, the result of using AssertIsString is the type true. Otherwise, it is the type <a href="https://www.typescriptlang.org/docs/handbook/basic-types.html#never" target="_blank">never</a>. If a variable has that type then no real value can be assigned to it.

This is an example of using AssertIsString – we want to check that strValue does have the statically inferred type string (or a subtype):

const strValue = 'abc';
const cond1: AssertIsString<typeof strValue> = true;
  // No type error: the assertion is true

The next example checks if numberValue does have the type string and fails.

const numberValue = 123;
// @ts-ignore: Type 'true' is not assignable to type 'never'.
const cond2: AssertIsString<typeof numberValue> = true;

As an aside, the following simplification of this technique does not work:

type cond3 = AssertIsString<typeof numberValue>;
  // type cond3 = never;

The condition of AssertIsString goes to the else branch, but the resulting type never is not in conflict with anything and does not produce an error.

3 - Extracting return types

TypeScript provides several parameterized utility types. One of them is relevant for us here:

ReturnType<T> 

Its result is the return type of a function type. In the next example, we use it to assert that the return type of twice() is string:

function twice(x: string) {
  return x + x;
}
const cond: AssertIsString<ReturnType<typeof twice>> = true;

4 - Generic type tests

The following parameterized type builds on the ideas we have already seen and checks whether a given type T is equal to an expected type Expected:

type AssertEqual<T, Expected> =
  T extends Expected
  ? (Expected extends T ? true : never)
  : never;

We are checking two conditions:

  • Is T a subtype of Expected?
  • Is Expected a subtype of T?

If both are true then T is equal to Expected.

AssertEqual in action

The following example uses AssertEqual:

function createPoint(x: number, y: number) {
  return {x, y};
}

const cond1: AssertEqual<
    typeof createPoint,
    (x: number, y: number) => {x: number, y: number}
  > = true;

We can also just check the inferred return type:

const cond2: AssertEqual<
    ReturnType<typeof createPoint>,
    {x: number, y: number}
  > = true;

typescript javascript angular testing

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Basic Introduction to Unit Testing in Angular

What is Unit Testing ? Unit testing is testing a unit in an isolated environment. A unit can be a class, component, service, directive module etc. which can

Install Angular - Angular Environment Setup Process

Install Angular in easy step by step process. Firstly Install Node.js & npm, then Install Angular CLI, Create workspace and Deploy your App.

JavaScript Vs TypeScript

Get to know here difference between JavaScript & TypeScript, In this blog explained with pros and cons of TypeScript & JavaScript.

Basics of Angular: Part-1

What is Angular? What it does? How we implement it in a project? So, here are some basics of angular to let you learn more about angular. Angular is a Typesc

An Introduction to Unit Testing in Angular

Unit testing, as the name implies, is about testing individual units of code. Unit tests try to answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list in the right order