2

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 ?

2
  • checkType(b, (a) => isNumber(a) || isString((a)) Commented Oct 8, 2022 at 11:40
  • Thanks, but I want dynamic type! (I edited my question) Commented Oct 8, 2022 at 11:46

1 Answer 1

5

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

Sign up to request clarification or add additional context in comments.

1 Comment

You were faster :D , upvoted. Here is my example with assert function, since OP wants to throw an error

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.