2 min read

Testing Typescript Types

The ability to inform and educate us on how a package functions and behaves through the use of types is one of Typescript’s biggest strengths as a language, alongside its’ ability to gradually be adopted within an existing Javascript project.

Unfortunately, we can’t always expect that those types are accurate. Some libraries were not initially built in Typescript, and so can’t entirely rely on the --declaration flag available in the Typescript compiler to handle the creation of accurate types. As a result, the types for these libraries may have been manually written to some extent, and may not be correct.

The bugs that can result from incorrect types can be insidious and time consuming, as our default as developers is generally to assume that our code is incorrect — at least initially.

As authors of libraries that may be in a period of transition from Javascript (or some language that compiles to Javascript) towards Typescript, it greatly benefits consumers of your libraries to have some testing in place to ensure that the types specified are correct as your library continues to evolve.

Available Tooling

There’s a couple of popular tools out there that help facilitate type testing — dtslint and tsd

dtslint

dtslint is a tool developed and supported by Microsoft. It states that its’ goal is to test for ‘style and correctness’, and achieves this by essentially doing a string comparison of the types. What this looks like in practice is the following:

helloWorld(); // $ExpectType string

It should be noted that while the tests are type-checked, they aren’t run, and so if you’re looking to execute the test as well as type check, you might be better off explicitly defining expected types for variables within the context of a standard unit test. For example:

const response: String = helloWorld();
expect(response).toEqual(‘hello world!’)’

One benefit to using dtslint over an alternative tool such as tsd (more on it in the following section) is that it’s designed to run the type declarations through multiple versions of Typescript, allowing you to more easily support older versions if required.

tsd

tsd differs from dtslint in that, rather than having the assertions written as a comment within the code, it uses methods. For example:

import { expectType } from ‘tsd’;
expectType<string>(helloWorld());

This design decision results in a small, but not insignificant, benefit over dtslint in that it’s not possible to introduce an unnoticed typo in the assertion. tsd also claims to assert on the ‘real types’ (something that comes up in the context of observables as pointed out in the project), but I have not gotten around to writing a test that exercises that behaviour myself and can’t speak on this from personal experience.

Conclusion

The main value derived from tsd and dtslint, and this type of testing in general, is when you’re publishing a package that is not fully written in Typescript such that you can’t rely on the built-in tooling to generate your package’s types and need to ensure that the correct typings are being communicated towards the users of the package.

This may not necessarily be worth the time investment depending on the amount of users of your package, and whether they are internal or external to your company, and what other testing you already have in place. However, these tools can be helpful if you want to allow those who are building Typescript projects to more easily use your package, even if you may or may not be converting your project to Typescript.


📫
Enjoy this post? Subscribe to be notified when I publish new content!

Resources/References

dtslint resources

tsd resources

Comparisons of dtslint vs tsd