Home > Software design >  typescript multiple type check function
typescript multiple type check function

Time:10-09

Background

I'm making type checking function with typescript.

const checkType = <T>(value: unknown, isTypeFn: (value: unknown) => value is T): T => {
  if (!isTypeFn(value)) {
    console.error(`${isTypeFn} ${value} does not have correct type`);
    throw new Error(`${value} does not have correct type`);
  }

  return value;
};

const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean';

const isString = (value: unknown): value is string => typeof value === 'string';

const isNumber = (value: unknown): value is number => typeof value === 'number';

const isNull = (value: unknown): value is null => value === null;

And I can use it like below.

const a = await getNumber() // this should be number
const numA: number = checkType(a, isNumber); // numA is number or throw Error!

Problem

I want to extend the checkType function like below.

const b = await getNumberOrString();
const bNumOrString: number | string = checkType(b, isNumber, isString);
const bNumOrBoolean: number | boolean = checkType(b, isNumber, isBoolean);
const bStringOrNull: string | null = checkType(b, isString, isNull);

How to improve the checkType for it to work like this ?

CodePudding user response:

The function checkType will need a rest parameter. Let's call it isTypeFns. isTypeFns is a generic type parameter T which will be an array of functions with type predicates.

const checkType = <
  T extends ((value: unknown) => value is any)[]
>(value: unknown, ...isTypeFns: T): CheckType<T> => {
  if (isTypeFns.some(fn => fn(value))) {
    console.error(`${value} does not have correct type`);
    throw new Error(`${value} does not have correct type`);
  }

  return value as CheckType<T>;
};

The implementation is straight forward. You just need to check if one of the functions in isTypeFns returns true given the value.

The return type gets trickier again. We need to take T and infer the union of type predicate types.

type CheckType<T extends ((value: unknown) => value is any)[]> = 
  T[number] extends ((value: unknown) => value is infer U) 
    ? U 
    : never

We use this for the return type of the function. TypeScript does not understand this complex type when it comes to the return statement of the implementation. That's why I added an assertion there.

const b = "b" as number | string;

const bNumOrString = checkType(b, isNumber, isString);
//    ^? const bNumOrString: string | number

const bNumOrBoolean = checkType(b, isNumber, isBoolean);
//    ^? const bNumOrBoolean: number | boolean

const bStringOrNull = checkType(b, isString, isNull);
//    ^? const bStringOrNull: string | null

Playground

  • Related