You can add tests for your static TS types right next to your regular Jest tests
Have you ever toiled to get the TypeScript types just right on a particular function and thought to yourself, “I wish I could write a test to make sure that I don’t break this type definition next time I edit this function”? Me too! In this post I’ll give you thorough instructions on how you can do just that.
SetupSection titled: Setup
The library we’re going to use to execute these static type tests is one that I made called jest-tsd. It’s a wrapper around tsd to make it really easy to use with Jest.
-
Install jest-tsd and its peer dependency.
-
Exclude
.test-d.ts
files from your TypeScript compiler build.- This is important because your type definition test files will intentionally add tests that throw TS compiler errors
- In your
tsconfig.json
, add:
-
Add a new Jest test for your type definitions
The Function Under TestSection titled: The Function Under Test
I want to use a non-trivial example type definition in this post so that I can write some non-trivial type tests. The rest of this section is explaining how that function works, feel free to skip if you don’t care.
The function I’ve chosen here, initTranslation
takes 1 or 2 arguments. Its purpose is to return a translate
function that can be called to map a translation key to text that can be shown on a UI for a particular language or region. The optional second parameter provides a way to avoid defining the same UI string multiple times.
- The first argument is a dictionary object whose keys & values are all strings.
- The second argument is a function with a
link()
param that returns a similar dictionary.- The difference is that this second object can invoke the
link()
function as a value of the object. link()
gets passed a key from the first parameter ofinitTranslation
in order to reuse that already defined UI string.- In the actual implementation,
link()
is secretly an identity function. Its only purpose is to provide strong typing of translation keys. Changing a linked key should throw a type error.
- The difference is that this second object can invoke the
Here’s a simple example of how it might be used:
Writing a Type Definition testSection titled: Writing a Type Definition test
jest-tsd
has a variety of different assertions available to you. Here we’ll make use of expectType
and expectError
.
First a series of tests for the single parameter version of initTranslation
:
And then similar tests for the two param version:
Finally test to ensure that the link()
function will only accept keys previously defined in the first param dictionary:
Tests in TypeScript?Section titled: Tests in TypeScript?
I can hear you saying, “but Zach, you made a big deal in your post on Jest testing mistakes saying that tests should always be written in JavaScript, not TypeScript!”.
And I stand by that. Writing tests in TS makes them harder to maintain over time and most of the time adds no value over their JS counterparts.
The actual Jest test file described above is still written in JavaScript!
ConclusionSection titled: Conclusion
Next time you take the time to write complicated generic types for some utility function in your shared code, I hope you’ll also take a little time to write tests for those types!