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.
More Tips
Type Predicates
1 min
TypeScript 5.1 Beta is OUT!
2 mins
How to Name your Types
4 mins
Don't use return types, unless...
4 mins
TypeScript 5.0 Beta Deep Dive
6 mins
Conform a Derived Type Without Losing Its Literal Values
1 min
Avoid unexpected behavior of React’s useState
1 min
Understand assignability in TypeScript
2 mins
Compare function overloads and generics
1 min
Use infer in combination with string literals to manipulate keys of objects
1 min
Access deeper parts of objects and arrays
1 min
Ensure that all call sites must be given value
1 min
Understand how TypeScript infers literal types
1 min
Get a TypeScript package ready for release to NPM in under 2 minutes
1 min
Use assertion functions inside classes
1 min
Assign local variables to default generic slots to dry up your code and improve performance
2 mins
Know when to use generics
2 mins
Map over a union type
1 min
Make accessing objects safer by enabling 'noUncheckedIndexedAccess' in tsconfig
1 min
Use generics to dynamically specify the number, and type, of arguments to functions
1 min
Use 'declare global' to allow types to cross module boundaries
2 mins
Turn a module into a type
2 mins
Create autocomplete helper which allows for arbitrary values
2 mins
Use deep partials to help with mocking an entity
1 min
Create a 'key remover' function which can process any generic object
1 min
Use generics in React to make dynamic and flexible components
1 min
Create your own 'objectKeys' function using generics and the 'keyof' operator
1 min
Write your own 'PropsFrom' helper to extract props from any React component
1 min
Use 'extends' keyword to narrow the value of a generic
1 min
Use function overloads and generics to type a compose function
2 mins
Decode URL search params at the type level with ts-toolbelt
2 mins
Use 'in' operator to transform a union to another union
2 mins
Derive a union type from an object
2 mins