Reducet

Conversions in TypeScript with ts-conversions

Although TypeScript provides extensive type safety, sometimes you have to repeat the same boilerplate code over and over in order satisfy the type checker. The @luizffgv/ts-conversions NPM package was created to provide simple functions that make working with TypeScript a quicker and more comfortable experience. Here we will take a look at the package’s few functions.

The uncheckedCast function

It’s not uncommon for TypeScript code to be riddled with unsafe type assertions; although it’s desirable to reduce such occurrences to a minimum, there may still be some left. That’s why uncheckedCast exists.

uncheckedCast is intended to replace the as keyword. Here is one way of using uncheckedCast to assert the tag type of a queried HTML element, with the target type specified as a type argument.

// type: HTMLElement | null
const input = document.getElementById("input");

// type: HTMLInputElement
const input2 = uncheckedCast<HTMLInputElement>(input);

Not only can uncheckedCast be used to assert a manually specified type, the type can also be inferred by omitting the type argument.

function printValue(element: HTMLInputElement) {
  console.log(element.value);
}

// type: HTMLElement | null
const input = document.getElementById("input");

// Here uncheckedCast has its type parameter inferred as HTMLInputElement
// because of the `printValue` function signature.
printValue(uncheckedCast(input));

Occurrences of uncheckedCast can be easily found in code and they also look ugly, as code smells should.

The trySpecify function

uncheckedCast doesn’t provide any kind of type checking, so it should be used sparingly. A safe alternative to uncheckedCast is trySpecify, which refines types down the inheritance tree.

trySpecify takes a value and a constructor, then returns the value with its type further refined as the constructor’s class type. If the value is not an instance of that type, the function throws.

// type: HTMLElement | null
const input = document.getElementById("input");

// type: HTMLInputElement
const refined = trySpecify(input, HTMLInputElement);

The return type of a trySpecify call is never if, from a type-checking standpoint, the call is guaranteed to throw. This happens when you try to refine a value to a type that’s not part of its inheritance tree.

// type: HTMLElement | null
const input = document.getElementById("input");

// type: never
const refined = trySpecify(input, Number);

The throwIfNull function

It’s pretty common to throw if a required value is nullish. throwIfNull simply provides a one-liner for doing just that.

// type: HTMLElement | null
const input = document.getElementById("input");

// type: HTMLElement
const input2 = throwIfNull(input);

The function name is slightly misleading, as it also throws then the value is undefined.

const map = new Map<number, number>();

// type: number | undefined
const number = map.get(0);

// type: number
const number2 = throwIfNull(number);

Wrapping things up

These functions might not seem like a lot but I began using them in most of my projects. They are simple, easily adaptable and work with JavaScript. Even though you may not want to use that package, I strongly suggest you to create functions with JSDoc or TypeScript to better deal with common type transformations.