asserttt
v1.0.1
Published
---
Readme
asserttt: minimal API for testing types
Table of contents:
Installation
npm install assertttasserttt has no non-dev dependencies!
Use cases
- Testing types
- Documenting TypeScript with code examples (e.g. via Markcheck)
- Support for TypeScript exercises, such as the ones provided by
Type<Challenge[]>. - Testing that related types are consistent in normal code
Complementary tools
If a file contains type tests, it’s not enough to run it, we must also type-check it:
- tsx (TypeScript Execute) is a tool that type-checks files before running them.
- It works well with the Mocha test runner: example setup
- ts-expect-error performs two tasks:
- Checking if each
@ts-expect-errorannotation prevents the right kind of error - Optional: reporting errors detected by TypeScript
- Checking if each
- Markcheck tests Markdown code blocks.
Usage
import { type Assert, assertType, type Assignable, type Equal, type Extends, type Includes, type Not } from 'asserttt';
//========== Asserting types: Assert<B> ==========
{
type Pair<X> = [X, X];
type _1 = Assert<Equal<
Pair<'a'>, ['a', 'a']
>>;
type _2 = Assert<Not<Equal<
Pair<'a'>, ['x', 'x']
>>>;
}
{
type _ = [
Assert<Assignable<number, 123>>,
Assert<Extends<Array<string>, Object>>,
Assert<Not<Extends<Array<string>, RegExp>>>,
Assert<Includes<'a'|'b', 'a'>>,
Assert<Includes<'a'|'b'|'c', 'a'|'c'>>,
];
}
//========== Asserting types of values: assertType<T>(v) ==========
const n = 3 + 1;
assertType<number>(n);API
Asserting
Assert<B>assertType<T>(value)
Included predicates (boolean results)
Equality:
Equal<X, Y>MutuallyAssignable<X, Y>PedanticEqual<X, Y>
Comparing/detecting types:
Extends<Sub, Super>Assignable<Target, Source>Includes<Superset, Subset>IsAny<T>
Boolean operations:
Not<B>
How does the code work?
Determining if two types are equal
MutuallyAssignable
export type MutuallyAssignable<X, Y> =
[X, Y] extends [Y, X] ? true : false
;- The brackets on the left-hand side of
extendsprevent distributivity. - Almost what we want for checking equality, but
anyis equal to all types – which is problematic when testing types.
Equal: like MutuallyAssignable but any is only equal to itself
This Equal predicate works well for many use cases:
type Equal<X, Y> =
[IsAny<X>, IsAny<Y>] extends [true, true] ? true
: [IsAny<X>, IsAny<Y>] extends [false, false] ? MutuallyAssignable<X, Y>
: false
;
type IsAny<T> = 0 extends (1 & T) ? true : false;PedanticEqual: a popular hack with several downsides
type PedanticEqual<X, Y> =
(<T>() => T extends X ? 1 : 2) extends // (A)
(<T>() => T extends Y ? 1 : 2) ? true : false // (B)
;It was suggested by Matt McCutchen (source). How does it work (source)?
In order to check whether the function type in line A extends the function type in line B, TypeScript has to compare the following two conditional types:
T extends X ? 1 : 2
T extends Y ? 1 : 2Since T does not have a value, both conditional types are deferred. Assignability of two deferred conditional types is computed via the internal function isTypeIdenticalTo() and only true if:
- Both have the same constraint.
- Their “then” branches have the same type and their “else” branches have the same type.
Thanks to #1, X and Y are compared precisely.
This hack has several downsides: See test/pedantic-equal_test.ts for more information.
Asserting
type Assert<_T extends true> = void;Alas, we can’t conditionally produce errors at the type level. That’s why we need to resort to a type parameter whose extends constraint requires it to be assignable to true.
(Idea by Blaine Bublitz)
Related work
Package ts-expect inspired this package. It’s very similar. This package uses different names and has a utility type
Assert(which doesn’t produce runtime code):type _ = Assert<Equal<X,Y>>; // asserttt expectType<TypeEqual<X, Y>>(true); // ts-expectThe type-challenges repository has a module with utility types for exercises. How is asserttt different?
- Smaller API
- Different names
- Implements boolean NOT via a helper type
Not(vs. two versions of the same utility type).
eslint-plugin-expect-type supports an elegant notation but requires a special tool (eslint) for checking.
