Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
expect-type
Advanced tools
Compile-time tests for types. Useful to make sure types don't regress into being overly-permissive as changes go in over time.
The expect-type npm package is a TypeScript utility that allows developers to assert and validate types at compile time. It is particularly useful for ensuring that types are as expected during development, which can help catch type-related errors early.
Type Assertion
This feature allows you to assert that a given value matches a specific type. In this example, the `expectType` function is used to assert that the `user` object conforms to the `User` interface.
import { expectType } from 'expect-type';
interface User {
name: string;
age: number;
}
const user: User = { name: 'Alice', age: 30 };
expectType<User>(user);
Type Equality
This feature allows you to check if two types are equal. In this example, `expectType` is used to assert that types `A` and `B` are equal.
import { expectType } from 'expect-type';
type A = { a: string };
type B = { a: string };
expectType<A, B>(true);
Type Inference
This feature allows you to assert the inferred type of a value. In this example, `expectType` is used to assert that the result of the `add` function is a `number`.
import { expectType } from 'expect-type';
function add(a: number, b: number) {
return a + b;
}
const result = add(1, 2);
expectType<number>(result);
The `tsd` package is a TypeScript type assertion library that allows you to write tests for your type definitions. It is similar to `expect-type` in that it helps ensure type correctness, but it is more focused on writing test cases for type definitions.
The `typescript-is` package provides runtime type checking for TypeScript. Unlike `expect-type`, which is used for compile-time type assertions, `typescript-is` allows you to perform type checks at runtime, making it useful for scenarios where you need to validate types dynamically.
The `io-ts` package is a runtime type system for IO decoding/encoding with TypeScript. It allows you to define codecs that can validate and decode data at runtime. While `expect-type` focuses on compile-time type assertions, `io-ts` provides a way to ensure type safety at runtime.
Compile-time tests for types. Useful to make sure types don't regress into being overly-permissive as changes go in over time.
Similar to Jest's expect
, but with type-awareness. Gives you access to a number of type-matchers that let you make assertions about the form of a reference or generic type parameter.
import {foo, bar} from '../foo'
import {expectTypeOf} from 'expect-type'
test('foo types', () => {
// make sure `foo` has type {a: number}
expectTypeOf(foo).toMatchTypeOf<{a: number}>()
// make sure `bar` is a function taking a string:
expectTypeOf(bar).parameter(0).toBeString()
expectTypeOf(bar).returns.not.toBeAny()
})
It can be used in your existing test files - or any other type-checked file you'd like - it's built into existing tooling with no dependencies. No extra build step, cli tool, IDE extension, or lint plugin is needed. Just import the function and start writing tests. Failures will be at compile time - they'll appear in your IDE and when you run tsc
.
See below for lots more examples.
npm install expect-type
import {expectTypeOf} from 'expect-type'
The expectTypeOf
method takes a single argument, or a generic parameter. Neither it, nor the functions chained off its return value, have any meaningful runtime behaviour. The assertions you write will be compile-time errors if they don't hold true.
Check an object's type with .toEqualTypeOf
:
expectTypeOf({a: 1}).toEqualTypeOf<{a: number}>()
.toEqualTypeOf
can check that two concrete objects have equivalent types:
expectTypeOf({a: 1}).toEqualTypeOf({a: 1})
.toEqualTypeOf
succeeds for objects with different values, but the same type:
expectTypeOf({a: 1}).toEqualTypeOf({a: 2})
.toEqualTypeOf
fails on extra properties:
// @ts-expect-error
expectTypeOf({a: 1, b: 1}).toEqualTypeOf<{a: number}>()
To allow for extra properties, use .toMatchTypeOf
. This checks that an object "matches" a type. This is similar to jest's .toMatchObject
:
expectTypeOf({a: 1, b: 1}).toMatchTypeOf({a: 1})
Another example of the difference between .toMatchTypeOf
and .toEqualTypeOf
, using generics. .toMatchTypeOf
can be used for "is-a" relationships:
type Fruit = {type: 'Fruit'; edible: boolean}
type Apple = {type: 'Fruit'; name: 'Apple'; edible: true}
expectTypeOf<Apple>().toMatchTypeOf<Fruit>()
// @ts-expect-error
expectTypeOf<Fruit>().toMatchTypeOf<Apple>()
// @ts-expect-error
expectTypeOf<Apple>().toEqualTypeOf<Fruit>()
Assertions can be inverted with .not
:
expectTypeOf({a: 1}).not.toMatchTypeOf({b: 1})
.not
can be easier than relying on // @ts-expect-error
:
type Fruit = {type: 'Fruit'; edible: boolean}
type Apple = {type: 'Fruit'; name: 'Apple'; edible: true}
expectTypeOf<Apple>().toMatchTypeOf<Fruit>()
expectTypeOf<Fruit>().not.toMatchTypeOf<Apple>()
expectTypeOf<Apple>().not.toEqualTypeOf<Fruit>()
Catch any/unknown/never types:
expectTypeOf<unknown>().toBeUnknown()
expectTypeOf<any>().toBeAny()
expectTypeOf<never>().toBeNever()
// @ts-expect-error
expectTypeOf<never>().toBeNumber()
.toEqualTypeOf
distinguishes between deeply-nested any
and unknown
properties:
expectTypeOf<{deeply: {nested: any}}>().not.toEqualTypeOf<{deeply: {nested: unknown}}>()
Test for basic javascript types:
expectTypeOf(() => 1).toBeFunction()
expectTypeOf({}).toBeObject()
expectTypeOf([]).toBeArray()
expectTypeOf('').toBeString()
expectTypeOf(1).toBeNumber()
expectTypeOf(true).toBeBoolean()
expectTypeOf(() => {}).returns.toBeVoid()
expectTypeOf(Promise.resolve(123)).resolves.toBeNumber()
expectTypeOf(Symbol(1)).toBeSymbol()
Nullable types:
expectTypeOf(undefined).toBeUndefined()
expectTypeOf(undefined).toBeNullable()
expectTypeOf(undefined).not.toBeNull()
expectTypeOf(null).toBeNull()
expectTypeOf(null).toBeNullable()
expectTypeOf(null).not.toBeUndefined()
expectTypeOf<1 | undefined>().toBeNullable()
expectTypeOf<1 | null>().toBeNullable()
expectTypeOf<1 | undefined | null>().toBeNullable()
More .not
examples:
expectTypeOf(1).not.toBeUnknown()
expectTypeOf(1).not.toBeAny()
expectTypeOf(1).not.toBeNever()
expectTypeOf(1).not.toBeNull()
expectTypeOf(1).not.toBeUndefined()
expectTypeOf(1).not.toBeNullable()
Use .extract
and .exclude
to narrow down complex union types:
type ResponsiveProp<T> = T | T[] | {xs?: T; sm?: T; md?: T}
const getResponsiveProp = <T>(props: T): ResponsiveProp<T> => ({})
type CSSProperties = {margin?: string; padding?: string}
const cssProperties: CSSProperties = {margin: '1px', padding: '2px'}
expectTypeOf(getResponsiveProp(cssProperties))
.exclude<unknown[]>()
.exclude<{xs?: unknown}>()
.toEqualTypeOf<CSSProperties>()
expectTypeOf(getResponsiveProp(cssProperties))
.extract<unknown[]>()
.toEqualTypeOf<CSSProperties[]>()
expectTypeOf(getResponsiveProp(cssProperties))
.extract<{xs?: any}>()
.toEqualTypeOf<{xs?: CSSProperties; sm?: CSSProperties; md?: CSSProperties}>()
expectTypeOf<ResponsiveProp<number>>().exclude<number | number[]>().toHaveProperty('sm')
expectTypeOf<ResponsiveProp<number>>().exclude<number | number[]>().not.toHaveProperty('xxl')
.extract
and .exclude
return never if no types remain after exclusion:
type Person = {name: string; age: number}
type Customer = Person & {customerId: string}
type Employee = Person & {employeeId: string}
expectTypeOf<Customer | Employee>().extract<{foo: string}>().toBeNever()
expectTypeOf<Customer | Employee>().exclude<{name: string}>().toBeNever()
Make assertions about object properties:
const obj = {a: 1, b: ''}
// check that properties exist (or don't) with `.toHaveProperty`
expectTypeOf(obj).toHaveProperty('a')
expectTypeOf(obj).not.toHaveProperty('c')
// check types of properties
expectTypeOf(obj).toHaveProperty('a').toBeNumber()
expectTypeOf(obj).toHaveProperty('b').toBeString()
expectTypeOf(obj).toHaveProperty('a').not.toBeString()
.toEqualTypeOf
can be used to distinguish between functions:
type NoParam = () => void
type HasParam = (s: string) => void
expectTypeOf<NoParam>().not.toEqualTypeOf<HasParam>()
But often it's preferable to use .parameters
or .returns
for more specific function assertions:
type NoParam = () => void
type HasParam = (s: string) => void
expectTypeOf<NoParam>().parameters.toEqualTypeOf<[]>()
expectTypeOf<NoParam>().returns.toBeVoid()
expectTypeOf<HasParam>().parameters.toEqualTypeOf<[string]>()
expectTypeOf<HasParam>().returns.toBeVoid()
More examples of ways to work with functions - parameters using .parameter(n)
or .parameters
, and return values using .returns
:
const f = (a: number) => [a, a]
expectTypeOf(f).toBeFunction()
expectTypeOf(f).toBeCallableWith(1)
expectTypeOf(f).not.toBeAny()
expectTypeOf(f).returns.not.toBeAny()
expectTypeOf(f).returns.toEqualTypeOf([1, 2])
expectTypeOf(f).returns.toEqualTypeOf([1, 2, 3])
expectTypeOf(f).parameter(0).not.toEqualTypeOf('1')
expectTypeOf(f).parameter(0).toEqualTypeOf(1)
expectTypeOf(1).parameter(0).toBeNever()
const twoArgFunc = (a: number, b: string) => ({a, b})
expectTypeOf(twoArgFunc).parameters.toEqualTypeOf<[number, string]>()
You can also check type guards & type assertions:
const assertNumber = (v: any): asserts v is number => {
if (typeof v !== 'number') {
throw new TypeError('Nope !')
}
}
expectTypeOf(assertNumber).asserts.toBeNumber()
const isString = (v: any): v is string => typeof v === 'string'
expectTypeOf(isString).guards.toBeString()
Assert on constructor parameters:
expectTypeOf(Date).toBeConstructibleWith('1970')
expectTypeOf(Date).toBeConstructibleWith(0)
expectTypeOf(Date).toBeConstructibleWith(new Date())
expectTypeOf(Date).toBeConstructibleWith()
expectTypeOf(Date).constructorParameters.toEqualTypeOf<[] | [string | number | Date]>()
Class instance types:
expectTypeOf(Date).instance.toHaveProperty('toISOString')
Promise resolution types can be checked with .resolves
:
const asyncFunc = async () => 123
expectTypeOf(asyncFunc).returns.resolves.toBeNumber()
Array items can be checked with .items
:
expectTypeOf([1, 2, 3]).items.toBeNumber()
expectTypeOf([1, 2, 3]).items.not.toBeString()
Check that functions never return:
const thrower = () => {
throw Error('oh no')
}
expectTypeOf(thrower).returns.toBeNever()
Generics can be used rather than references:
expectTypeOf<{a: string}>().not.toEqualTypeOf<{a: number}>()
Distinguish between missing/null/optional properties:
expectTypeOf<{a?: number}>().not.toEqualTypeOf<{}>()
expectTypeOf<{a?: number}>().not.toEqualTypeOf<{a: number}>()
expectTypeOf<{a?: number}>().not.toEqualTypeOf<{a: number | undefined}>()
expectTypeOf<{a?: number | null}>().not.toEqualTypeOf<{a: number | null}>()
expectTypeOf<{a: {b?: number}}>().not.toEqualTypeOf<{a: {}}>()
Detect the difference between regular and readonly properties:
type A1 = {readonly a: string; b: string}
type E1 = {a: string; b: string}
expectTypeOf<A1>().toMatchTypeOf<E1>()
expectTypeOf<A1>().not.toEqualTypeOf<E1>()
type A2 = {a: string; b: {readonly c: string}}
type E2 = {a: string; b: {c: string}}
expectTypeOf<A2>().toMatchTypeOf<E2>()
expectTypeOf<A2>().not.toEqualTypeOf<E2>()
eslint-plugin-jest
If you're using Jest along with eslint-plugin-jest
, you will get warnings from the jest/expect-expect
rule, complaining that "Test has no assertions" for tests that only use expectTypeOf()
.
To remove this warning, configure the ESlint rule to consider expectTypeOf
as an assertion:
"rules": {
// ...
"jest/expect-expect": [
"warn",
{
"assertFunctionNames": [
"expect", "expectTypeOf"
]
}
],
// ...
}
Other projects with similar goals:
tsd
is a CLI that runs the TypeScript type checker over assertionsts-expect
exports several generic helper types to perform type assertionsdtslint
does type checks via comment directives and tslinttype-plus
comes with various type and runtime TypeScript assertionsstatic-type-assert
type assertion functionsThe key differences in this project are:
actual
and expected
clear. This is helpful with complex types and assertions.expectTypeOf(...).not
any
(as well as unknown
and never
) (see issues outstanding at time of writing in tsd for never and any).
not
, to protect against functions returning too-permissive types. For example, const parseFile = (filename: string) => JSON.parse(readFileSync(filename).toString())
returns any
, which could lead to errors. After giving it a proper return-type, you can add a test for this with expect(parseFile).returns.not.toBeAny()
expectTypeOf(square).toMatchTypeOf<Shape>()
tsc
.FAQs
[![CI](https://github.com/mmkal/expect-type/actions/workflows/ci.yml/badge.svg)](https://github.com/mmkal/expect-type/actions/workflows/ci.yml) ![npm](https://img.shields.io/npm/dt/expect-type) [![X (formerly Twitter) Follow](https://img.shields.io/twitte
The npm package expect-type receives a total of 888,102 weekly downloads. As such, expect-type popularity was classified as popular.
We found that expect-type demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.