Throw detailed error messages for type checks

You can use TimeScript to throw some absolutely crazy errors at the type level even without touching the runtime code at all.

Here in our deepEqualCompare function we use a === b which is really good for comparing primitives like two numbers or a string to another string but it's really not very good at comparing two arrays.


export const deepEqualCompare = <Arg>(a: Arg, b: Arg): boolean => {
if (Array.isArray(a) && Array.isArray(b)) {
throw new Error("You cannot compare two arrays using deepEqualCompare")
}
}
deepEqualCompare(1, 1)
deepEqualCompare([], ["a"])

The array comparison will always return false, because this first array is not the same element as the other array. We can actually move this to the type level.

first of all we're going to say check for bad args and we're going to say this is going to be. a generic and the arg is going to be like this. We're going to say arg extends any array and we're going to say you cannot compare two arrays using deepEqualCompare. We're just going to copy and. paste that in otherwise we're going to return the arg


type CheckForBadArgs<Arg> = Arg extends any[]
? "You cannot compare two arrays using deepEqualCompare"
: Arg

and we're going to use this in the function arguments of deepEqualCompare. Adding CheckForBadArgs to both a and b.


export const deepEqualCompare = <Arg>(
a: CheckForBadArgs<Arg>,
b: CheckForBadArgs<Arg>
): boolean => {
if (Array.isArray(a) && Array.isArray(b)) {
throw new Error("You cannot compare two arrays using deepEqualCompare")
}
}

And now what you'll see when we deepEqualCompare two arrays is an error saying, "Argument of type 'never[]' is not assignable to parameter of type ''You cannot compare two arrays using deepEqualCompare'".

So we've actually successfully moved this error to the type level!

Transcript

0:00 You can use TypeScript to throw some absolutely crazy errors at the type level, even without touching the runtime code at all. Here in our deepEqual compare function, we use this A triple equals B, which is really good for comparing primitives like 1 and 1, for instance, or a string to another string.

0:19 It's really not very good at comparing two arrays. This will always return false, for instance, even if it's like this because this first array is not the same element as the other array. We can actually move this to the type level by saying, first of all, we're going to say, "Check for bad args."

0:39 We're going to say, "This is going to be generic. The arg is going to be like this." We're going to say arg extends any array." We're going to say, "You cannot compare two arrays using deepEqual compare." We're just going to copy and paste that in.

0:53 Otherwise, we're going to return the arg. We're going to use this in the function arguments here. We're going to say, "Check for bad args here and check for bad args there." Now, what you'll see is that here, argument of type never is not assignable to parameter of type.

1:10 You cannot compare two arrays using deepEqual compare. We actually managed to move this to the type level.

Using a crazy trick I picked up from @AndaristRake, you can throw detailed error messages for type checks.

Here, I move a runtime check in a function to the type level, meaning you get a detailed error if you use it wrong.

Discuss on Twitter

More Tips

Assign local variables to default generic slots to dry up your code and improve performance