How to transform TS asserts condition in handy function?

70 Views Asked by At

I'm using asserts condition and have a code like this

enum Types { a = "a", b = "b", c = "c" }

type Entity = { type: Types };

assert(condition: any): asserts condition {
  if(!condition) throw new Error();
}

function abc(entity: Entity) {
  assert(entity.type === Types.a || entity.type === Types.b);
  ...
}

function def(entity: Entity) {
  assert(entity.type === Types.b || entity.type === Types.c);
  ...
}

function ghi(entity: Entity) {
  assert(entity.type === Types.c);
  ...
}

Is it possible to make compact generic assertion function to use like this and get the same assertion type checks?

assertType(entity, [Types.a, Types.b]);
2

There are 2 best solutions below

0
NtsDK On BEST ANSWER

Answers to this question gave me an idea of solution.

function checkObjectType<
    T extends { type: unknown }, 
    X, 
    Y = never, 
    Z = never
>(obj: T, types: readonly [X, Y?, Z?]): obj is S & { type: X | Y | Z } {
    return types.some((type) => type === obj.type;
}


// Then I can write code like this.
function abc(entity: Entity) {
  assert(checkObjectType(entity, [Types.a, Types.b] as const));
  ...
  // in Runtime we check assertion and get only types a and b here
  // in Compiletime TS knows that only types a and b can be expected here
}


1
Gabriel On

You can extend your function to 2 or 3 arguments by doing something like this :

enum Types {
  a = "a",
  b = "b",
  c = "c",
}

function assertType<T, U>(v: unknown, types: [T, U]): asserts v is T | U {
  if (v!==types[0] && v!==types[1]) throw new Error("Failed condition")
}

const typeToTest = Types.a;

assertType(typeToTest, [Types.a, Types.b]); // PASS
assertType(typeToTest, [Types.a, Types.c]); // PASS
assertType(typeToTest, [Types.b, Types.c]); // FAIL

However I don't think it's possible to extend it to an indefinite number of values in the array. It means you must have one function to test for 1 type, one function to test for 2 types...