Release Notes
This page lists breaking changes and notable new features. Please be sure to read the release notes before upgrading from previous version.
The detailed changelog can be found in TSTyche repository on GitHub .
TSTyche 4.3
TSTyche 4.3 ships with the new fixtureFileMatch
configuration option.
Configuration
fixtureFileMatch
The list of glob patterns to match the fixture files.
TSTyche 4.2
TSTyche 4.2 ships with glob brace expansion added.
Configuration
testFileMatch
Brace expansion of comma separated lists is now supported. For example, ["**/tests/*.{ts,tsx}"]
or ["**/tests/*.ts{,x}"]
both expand to ["**/tests/*.ts", "**/tests/*.tsx"]
.
TSTyche 4.1
TSTyche 4.1 ships with the new checkSuppressedErrors
configuration option.
Configuration
checkSuppressedErrors
Check errors silenced by // @ts-expect-error
directives in the test files.
When this option is enabled, the runner reads the text after // @ts-expect-error
and checks if it matches the actual error message:
// @ts-expect-error Cannot find name 'add'
console.log(add);
If a message does not match or is not specified, an error is reported.
To ignore a directive, append the !
character after it: // @ts-expect-error!
.
Compiler Options
The noUncheckedSideEffectImports
compiler option is now enabled for TypeScript 5.6 and above.
TSTyche 4.0
TSTyche 4.0 ships with the new .toBeApplicable
, .toBeCallableWith()
and .toBeConstructableWith()
matchers, support for template test files, enhanced protection against accidental any
or never
types and stricter default compiler options.
Requirements
Breaking! The minimum supported Node.js version is 20.9
. It is recommended to use the latest LTS release .
TypeScript Versions
Breaking! Support for TypeScript <=4.6
is dropped. Only version 4.7
or above can be used for testing.
Matchers
Breaking! The deprecated .toMatch()
matcher is removed.
Breaking! All primitive type matchers like .toBeString()
or .toBeNumber()
are removed. Use .toBe()
instead:
import { expect } from "tstyche";
- expect<string>().type.toBeString();
+ expect<string>().type.toBe<string>();
- expect<number>().type.toBeNumber();
+ expect<number>().type.toBe<number>();
.toBeApplicable
The new .toBeApplicable
matcher checks if the decorator function can be applied to a class or a class member.
Differently from other matchers, .toBeApplicable
must be used as a decorator:
import { expect, test } from "tstyche";
function bind<This, Value extends (this: This, ...args: any) => any>(
target: Value,
context: ClassMethodDecoratorContext<This, Value>,
) {
// ...
}
test("bind", () => {
class Fixture {
#name: string;
constructor(name: string) {
this.#name = name;
}
@(expect(bind).type.toBeApplicable) // <-- and here it is!
toString() {
return `Hello, my name is ${this.#name}.`;
}
}
});
.toBeCallableWith()
The new .toBeCallableWith()
matcher checks if a function is callable with the given arguments.
import { expect, test } from "tstyche";
function isSameLength<T extends { length: number }>(a: T, b: T) {
return a.length === b.length;
}
test("isSameLength", () => {
expect(isSameLength([1, 2], [1, 2, 3])).type.toBe<boolean>();
expect(isSameLength("one", "two")).type.toBe<boolean>();
expect(isSameLength).type.not.toBeCallableWith(1, 2);
expect(isSameLength).type.not.toBeCallableWith("zero", [123]);
expect(isSameLength<string | Array<number>>).type.toBeCallableWith("zero", [123]);
});
.toBeConstructableWith()
The new .toBeConstructableWith()
matcher checks if a class is constructable with the given arguments.
import { expect, test } from "tstyche";
class Pair<T> {
left: T;
right: T;
constructor(left: T, right: T) {
this.left = left;
this.right = right;
}
}
test("Pair", () => {
expect(Pair).type.toBeConstructableWith("sun", "moon");
expect(Pair).type.toBeConstructableWith(true, false);
expect(Pair).type.not.toBeConstructableWith("five", 10);
expect(Pair<number | string>).type.toBeConstructableWith("five", 10);
expect(Pair).type.not.toBeConstructableWith();
expect(Pair).type.not.toBeConstructableWith("nope");
});
Conditional Tests
The // @tstyche if
comment directive is added to allow conditionally skipping an expect()
, test()
or describe()
:
import { expect, test } from "tstyche";
function isUint8Array(target: unknown): target is Uint8Array {
return target instanceof Uint8Array;
}
test("isUint8Array", () => {
const unknowns: Array<unknown> = [];
// @tstyche if { target: [">=5.7"] } -- Before TypeScript 5.7, 'Uint8Array' was not generic
expect(unknowns.filter(isUint8Array)).type.toBe<Array<Uint8Array<ArrayBufferLike>>>();
// @tstyche if { target: ["<5.7"] }
expect(unknowns.filter(isUint8Array)).type.toBe<Array<Uint8Array>>();
});
If this directive is used at the top of the file, the whole test file would run only when the specified condition matches.
Template Test Files
The template test files allow generating tests using a template and data table.
To mark a test file as a template, add the // @tstyche template
comment directive at the top. When the directive is found, the default export of a file is interpreted as a type test text.
For example, the following:
// @tstyche template -- For documentation, see: https://tstyche.org/guides/template-test-files
let testText = `import { expect, test } from "tstyche";
`;
for (const source of ["string", "number"]) {
testText += `test("is ${source} a string?", () => {
expect<${source}>().type.toBe<string>();
});
`;
}
export default testText;
is interpreted as:
import { expect, test } from "tstyche";
test("is string a string?", () => {
expect<string>().type.toBe<string>();
});
test("is number a string?", () => {
expect<number>().type.toBe<string>();
});
To learn more, see the Template Test Files page.
Thanks to @nikelborm for the idea!
Configuration
checkSourceFiles
The checkSourceFiles
configuration option is enabled by default.
rejectAnyType
The rejectAnyType
configuration option is enabled by default.
rejectNeverType
The rejectNeverType
configuration option is enabled by default.
Command Line
--fetch
--fetch
is the new name of --install
.
Compiler Options
If your project does not have a TSConfig file, default compiler options now are much more strict:
{
"allowImportingTsExtensions": true, // not set for TypeScript 4.x
"allowJs": true,
"checkJs": true,
"exactOptionalPropertyTypes": true,
"jsx": "preserve",
"module": "nodenext",
"moduleResolution": "nodenext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"strict": true,
"target": "esnext",
"verbatimModuleSyntax": true // not set for TypeScript 4.x
}