Expect types
The @japa/expect-types plugin allows you to write assertions for TypeScript types. The plugin does not have any runtime behavior; instead, it reports errors when you either compile your TypeScript code or perform type-checking.
Installation
You can install the package from the npm registry as follows.
npm i -D @japa/expect-type
And register it as a plugin within the entry point file, i.e. (bin/test.js)
import { expectTypeOf } from '@japa/expect-type'
import { configure } from '@japa/runner'
configure({
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.
Fails, bar has an additional property
class Foo {}
class Bar {
foo = 1
}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
Fails, foo has an additional property
class Foo {
foo = 1
}
class Bar {}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
Passes, both objects are equal
class Foo {}
class Bar {}
expectTypeOf(new Foo()).toEqualTypeOf<Bar>()
toMatchTypeOf
The toMatchTypeOf method is the same as the toEqualTypeOf method but less strict. It allows the actual object to have additional properties.
class Foo {}
class Bar {
foo = 1
}
expectTypeOf(new Foo()).toMatchTypeOf<Bar>()
Passes, the foo is a superset of bar
class Foo {
foo = 1
}
class Bar {}
expectTypeOf(new Foo()).toMatchTypeOf<Bar>()
Passes, both are equal
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 the 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. 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 the 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 whether the class instance has a given property.
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()