Test TypeScript types at test runner runtime - Works seamlessly with Jest, Vitest, and Bun.
- Types Testing: Ensures TypeScript types or definitions are tested.
- Readable Tests: Uses the expect-assertion style for clean and easy-to-read types testing.
- Variative Assertions: Provides assertions like
toBe,toBeNever,toBeUnknown,toBeVoid,toBeAny, and more for checking. - Framework Agnostic: Seamlessly works with Jest (TypeScript), Vitest, Bun Test, and other TypeScript testing frameworks.
- Easy Integration: Integrates smoothly into TypeScript testing frameworks.
- No Special Comments Needed: No need to add special comment like
@ts-expect-error, just write clean and readable types tests code inside the existing test suite. - Runtime Stack Traces: Offers detailed stack traces for failed tests.
Here is an example of how types-testing tests TypeScript types or definitions (sources):
// example.test.tsimport{join}from'node:path';import{TypesTesting}from'src';importexampleModulefrom'./example-module';import*asExampleTypesfrom'./example-types';if(process.versions.bun){letbunTest=(awaitimport('bun:test')).default;if(!bunTest){bunTest=awaitimport('bun:test');}global.describe=bunTest.describeasunknownastypeofglobal.describe;global.test=bunTest.testasunknownastypeofglobal.test;}elseif(process.env.VITEST){constvitest=awaitimport('vitest');global.describe=vitest.describeasunknownastypeofglobal.describe;global.test=vitest.testasunknownastypeofglobal.test;}constbasePath=join(process.cwd(),'__tests__/__example');const{ expectType }=newTypesTesting({
basePath,tsConfig: 'tsconfig.json'}).prepare();describe('example-module',()=>{test(`exampleModule.name type is 'example-module'`,()=>{// using runtime argumentexpectType(exampleModule.name).toBe<'example-module'>();expectType(exampleModule.name).toBeStringLiteral();// using type argumentexpectType<typeofexampleModule.name>().toBe<'example-module'>();expectType<typeofexampleModule.name>().toBeString();});test(`exampleModule.getName type is function without parameter and return 'example-module'`,()=>{// using runtime argumentexpectType(exampleModule.getName).toBe<()=>'example-module'>();expectType(exampleModule.getName).toBe<{(): 'example-module';}>();expectType(exampleModule.getName()).toBe<'example-module'>();// using type argumentexpectType<typeofexampleModule.getName>().toBe<()=>'example-module'>();expectType<typeofexampleModule.getName>().toBe<{(): 'example-module';}>();expectType<ReturnType<typeofexampleModule.getName>>().toBe<'example-module'>();});if(process.env.FAIL_EXAMPLE_TEST){test('fail example',()=>{expectType(exampleModule.name).toBe<string>();});}});describe('example-types',()=>{test('KeyOf is utility types for getting keys from object type',()=>{expectType<ExampleTypes.KeyOf<{a: 1;b: 2;c: 3}>>().toBe<'a'|'b'|'c'>();});if(process.env.FAIL_EXAMPLE_TEST){test('fail example',()=>{expectType<ExampleTypes.KeyOf<{a: 1;b: 2;c: 3}>>().toBe<1|2|3>();});}});// example-module.jsexportdefault{name: 'example-module',getName(){returnthis.name;}};// example-module.d.tsdeclareconstexampleModule: {name: 'example-module';getName(): 'example-module';}exportdefaultexampleModule;// example-types.tsexporttypeKeyOf<T>=TextendsT ? keyofT : never;Result from Jest (29.7.0)
FAIL __tests__/__example/example.test.ts
example-module
√ exampleModule.name type is 'example-module' (12 ms)
√ exampleModule.getName type is function without parameter and return 'example-module' (5 ms)
× fail example (2 ms)
example-types
√ KeyOf is utility types for getting keys from object type (1 ms)
× fail example (1 ms)
● example-module › fail example
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: string
Received type: "example-module"
56 | if (process.env.FAIL_EXAMPLE_TEST) {
57 | test('fail example', () => {
> 58 | expectType(exampleModule.name).toBe<string>();
| ^
59 | });
60 | }
61 | });
at Object.<anonymous> (__tests__/__example/example.test.ts:58:38)
● example-types › fail example
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: 1 | 2 | 3
Received type: "a" | "b" | "c"
70 | if (process.env.FAIL_EXAMPLE_TEST) {
71 | test('fail example', () => {
> 72 | expectType<ExampleTypes.KeyOf<{ a: 1; b: 2; c: 3 }>>().toBe<1 | 2 | 3>();
| ^
73 | });
74 | }
75 | });
at Object.<anonymous> (__tests__/__example/example.test.ts:72:62)
Test Suites: 1 failed, 1 total
Tests: 2 failed, 3 passed, 5 total
Snapshots: 0 total
Time: 2.336 s, estimated 3 s
Result from Vitest (3.1.1)
RUN v3.1.2 /@codeismyid/types-testing
❯ __tests__/__example/example.test.ts (5 tests | 2 failed) 12ms
✓ example-module > exampleModule.name type is 'example-module' 6ms
✓ example-module > exampleModule.getName type is function without parameter and return 'example-module' 2ms
× example-module > fail example 2ms
→ expectType<received>().toBe<expected>()
Expected type: string
Received type: "example-module"
✓ example-types > KeyOf is utility types for getting keys from object type 0ms
× example-types > fail example 1ms
→ expectType<received>().toBe<expected>()
Expected type: 1 | 2 | 3
Received type: "a" | "b" | "c"
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
FAIL __tests__/__example/example.test.ts > example-module > fail example
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: string
Received type: "example-module"
❯ __tests__/__example/example.test.ts:55:38
56| if (process.env.FAIL_EXAMPLE_TEST) {
57| test('fail example', () => {
58| expectType(exampleModule.name).toBe<string>();
| ^
59| });
60| }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
FAIL __tests__/__example/example.test.ts > example-types > fail example
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: 1 | 2 | 3
Received type: "a" | "b" | "c"
❯ __tests__/__example/example.test.ts:69:62
70| if (process.env.FAIL_EXAMPLE_TEST) {
71| test('fail example', () => {
72| expectType<ExampleTypes.KeyOf<{ a: 1; b: 2; c: 3 }>>().toBe<1 | 2 | 3>();
| ^
73| });
74| }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
Test Files 1 failed (1)
Tests 2 failed | 3 passed (5)
Start at 21:39:24
Duration 2.78s (transform 185ms, setup 0ms, collect 2.33s, tests 12ms, environment 0ms, prepare 122ms)
Result from Bun Test (1.1.42)
bun test v1.1.42 (50eec002)
__tests__/__example/example.test.ts:
✓ example-module > exampleModule.name type is 'example-module' [16.00ms]
✓ example-module > exampleModule.getName type is function without parameter and return 'example-module' [15.00ms]
53 | >().toBe<'example-module'>();
54 | });
55 |
56 | if (process.env.FAIL_EXAMPLE_TEST) {
57 | test('fail example', () => {
58 | expectType(exampleModule.name).toBe<string>();
^
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: string
Received type: "example-module"
at /@codeismyid/types-testing/__tests__/__example/example.test.ts:58:38
✗ example-module > fail example [16.00ms]
✓ example-types > KeyOf is utility types for getting keys from object type
67 | >();
68 | });
69 |
70 | if (process.env.FAIL_EXAMPLE_TEST) {
71 | test('fail example', () => {
72 | expectType<ExampleTypes.KeyOf<{ a: 1; b: 2; c: 3 }>>().toBe<1 | 2 | 3>();
^
TypesTestingError: expectType<received>().toBe<expected>()
Expected type: 1 | 2 | 3
Received type: "a" | "b" | "c"
at /@codeismyid/types-testing/__tests__/__example/example.test.ts:72:62
✗ example-types > fail example [16.00ms]
3 pass
2 fail
Ran 5 tests across 1 files. [2.09s]
# NPM
npm install --save-dev types-testing
# BUN
bun add -d types-testingimport{expectType}from'types-testing';describe('test',()=>{test('test',()=>{expectType<string>().toBe<string>();});});Note:
preparefunction get invoked whenexpectTypeis called at the first time withprocess.cwd()as base path andtsconfig.jsonas TypeScript configuration file.
import{expectType,prepare}from'types-testing';prepare({basePath: 'this/is/basepath',tsConfig: 'tsconfig.test.json'});describe('test',()=>{test('test',()=>{expectType<string>().toBe<string>();});});import{expectType,prepare}from'types-testing';prepare({basePath: 'this/is/basepath',tsConfig: 'tsconfig.test.json',compilerOptions: {// add ts compiler options to extends typescript compiler options from tsconfig file},files: [// add included files to extends included files from tsconfig file],projectReferences: [// add project references to extends project references from tsconfig file.]});describe('test',()=>{test('test',()=>{expectType<string>().toBe<string>();});});import{expectType,prepare}from'types-testing';prepare({compilerOptions: {// add ts compiler options},files: [// add included files]})describe('test',()=>{test('test',()=>{expectType<string>().toBe<string>();});});import{TypesTesting}from'types-testing';const{ expectType, prepare }=newTypesTesting({autoPrepare: false,basePath: process.cwd(),tsConfig: 'tsconfig.test.json'});describe('test',()=>{beforeAll(()=>{prepare();});test('test',()=>{expectType<string>().toBe<string>();});});- Typescript - Strongly typed programming language that builds on JavaScript.
- Bun - All-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager.
The code in this project is released under the MIT License.