Expect types
The @japa/expect-types
plugin allows you to write an assertion for the TypeScript types. However, the plugin has no runtime behavior, and you must type-check your project for assertions to run.
You can install the package from the npm registry as follows.
npm i -D @japa/expect-type@1.0.3
And register it as a plugin within the bin/test.js
file.
import { expectTypeOf } from '@japa/expect-type'
import { configure, processCliArgs } from '@japa/runner'
configure({
...processCliArgs(process.argv.slice(2)),
...{
files: ['tests/**/*.spec.js'],
plugins: [expectTypeOf()]
}
})
const { expectTypeOf } = require('@japa/expect-type')
const { configure, processCliArgs } = require('@japa/runner')
configure({
...processCliArgs(process.argv.slice(2)),
...{
files: ['tests/**/*.spec.js'],
plugins: [expectTypeOf()]
}
})
Once done. You can access the expectTypeOf
property from the
Test context as follows.
test('add two numbers', ({ expectTypeOf }) => {
expectTypeOf({ foo: 'bar' }).toEqualTypeOf<{ foo: string }>()
})
The expectTypeOf
method accepts either an argument or a generic type. For example:
type User = { username: string, email: string, password?: string }
const user = { username: 'virk', email: 'virk@example.com' }
// Assert with value
expectTypeOf(user).toMatchTypeOf<User>()
type User = { username: string, email: string, password?: string }
const user = { username: 'virk', email: 'virk@example.com' }
// Assert with generic
expectTypeOf<typeof user>().toMatchTypeOf<User>()
The plugin is a wrapper on the expect-type npm package. Feel free to consult their README file as well.
toEqualTypeOf
Expect the actual value to have the same expected type. Having additional or fewer properties will make the assertion fail.
class Foo {}
class Bar {
foo = 1
}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
class Foo {
foo = 1
}
class Bar {}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
class Foo {}
class Bar {}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
toMatchTypeOf
The toMatchTypeOf
method is same as the toEqualTypeOf
method, but less strict. It allows the actual object to having additional properties.
class Foo {}
class Bar {
foo = 1
}
expectTypeOf(new Foo()).toMatchTypeOf<Bar>()
class Foo {
foo = 1
}
class Bar {}
expectTypeOf(new Foo()).toMatchTypeOf<Bar>()
class Foo {}
class Bar {}
expectTypeOf(new Foo()).toMatchTypeOf<Bar>()
toBeUnknown
Expect value to be of unknow
type.
const { data } = await got.post()
expectTypeOf(data).tobeUnknown()
toBeAny
Expect value to be of any
type.
const { data } = await got.post<any>()
expectTypeOf(data).toBeAny()
toBeNever
Expect value to be of the never
type.
const someVariable: never
expectTypeof(someVariable).toBeNever()
toBeFunction
Expect value to be of function
type.
expectTypeOf(() => {}).toBeFunction()
toBeObject
Expect value to be of object
type. Objects with any key-value pair are allowed.
class Foo {}
expectTypeOf(new Foo()).toBeObject()
const foo = {}
expectTypeOf(foo).toBeObject()
toBeArray
Expect the value to be of the array
type.
expectTypeOf([1, 2]).toBeArray()
toBeString
Expect the value to be of string
type.
expectTypeOf('hello world').toBeString()
toBeNumber
Expect the value to be of the number
type.
expectTypeOf(1).toBeNumber()
toBeBoolean
Expect the value to be of the boolean
type.
expectTypeOf(true).toBeBoolean()
expectTypeOf(false).toBeBoolean()
toBeSymbol
Expect value to be of symbol
type.
expectTypeOf(Symbol('foo')).toBeSymbol()
expectTypeOf(Symbol.for('foo')).toBeSymbol()
toBeUndefined
Expect the value to be of the undefined
type.
expectTypeOf(undefined).toBeUndefined()
toBeNullable
Expect the value to be of the null
type.
expectTypeOf(null).toBeNullable()
returns.TYPE
Assert the return value of a function. You can use all the above-documented assertion methods with the returns
modifier.
function foo () {}
expectTypeOf(foo).returns.toBeVoid()
function foo () {
return 'hello world.'
}
expectTypeOf(foo).returns.toBeString()
class Foo {}
function foo () {
return new Foo()
}
expectTypeOf(foo).returns.toEqualTypeOf<Foo>()
resolves.TYPE
The resolves
modifier is the same as the returns
modifier. But instead, it looks at the value of the resolved promise.
async function foo () {
return 'hello world.'
}
expectTypeOf(foo()).resolves.toBeString()
expectTypeOf(Promise.resolve(1)).resolves.toBeString()
You can also combine the returns
and resolves
modifiers to assert the return type value of an async function.
async function foo () {
return 'hello world.'
}
expectTypeOf(foo).returns.resolves.toBeString()
parameters.TYPE
Assert the type of function parameters.
function greetUser(name: string, age: number) {}
expectTypeOf(greetUser).parameters.toEqualTypeOf<[string, number]>()
Optionally, you can assert parameters at a specific position as well.
function greetUser(name: string, age: number) {}
expectTypeOf(greetUser).parameter(0).toBeString()
expectTypeOf(greetUser).parameter(1).toBeNumber()
not.TYPE
You can use the not
modifier to inverse the assertions.
expectTypeOf(1).not.toBeString()
expectTypeOf('hello world').not.toBeNumber()
You can also combine the returns
and the not
modifier to assert the return type of a given function.
function foo() {
return 'hello world.'
}
expectTypeOf(foo).returns.not.toBeAny()
exclude
You can use the exclude
method to narrow down complex unions. In the following example, we want to remove the union with the email: string
property and assert against the rest of the unions.
The exclude
method does not remove properties from an object. Instead, the method is used to narrow down types from a union.
type User =
| { email: string; age: number }
| { username: string; age: number }
expectTypeOf<User>()
.exclude<{ email: string }>()
.toEqualTypeOf<{ username: string; age: number }>()
type KeyName = string | number | symbol
expectTypeOf<KeyName>()
.exclude<string>()
.toEqualTypeOf<number | symbol>()
extract
The extract
method is the opposite of the exclude
method. Instead of discarding unions, it will assert against the matched unions.
type KeyName = string | number | symbol
expectTypeOf<KeyName>()
.extract<string>()
.toEqualTypeOf<string>()
constructor parameters
The constructorParameters
modifier is same as the
parameters modifier, but for the class constructor.
class User {
constructor (name: string, age: number) {}
}
expectTypeOf(User)
.constructorParameters
.toEqualTypeOf<[string, number]>()
You can use the toBeConstructibleWith
method to check if you can construct an instance of the class using specific values.
class User {
constructor (name: string, age: number) {}
}
expectTypeOf(User).toBeConstructibleWith('joda', 10)
instance.toHaveProperty
You can use the instance
modifier to assert if the class instance has a given property or not.
class User {
constructor(
private firstName: string,
private lastName: string
) {}
fullName(): string {
return `${this.firstName} ${this.lastName}`
}
}
expectTypeOf(User).instance.toHaveProperty('fullName')
// FAILS. "firstName" and "lastName" are private
expectTypeOf(User).instance.toHaveProperty('firstName')
expectTypeOf(User).instance.toHaveProperty('lastName')
items.TYPE
You can use the items
modifier on an array to assert the type of array members.
expectTypeOf([1, 2, 3]).items.toBeNumber()
expectTypeOf([1, 2, 3]).items.not.toBeString()