Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

expect-type

Package Overview
Dependencies
Maintainers
1
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

expect-type - npm Package Compare versions

Comparing version 0.16.0 to 0.17.0-1

177

dist/index.d.ts

@@ -12,2 +12,16 @@ export declare type Not<T extends boolean> = T extends true ? false : true;

export declare type IsNeverOrAny<T> = Or<[IsNever<T>, IsAny<T>]>;
export declare type BrandSpecial<T> = IsAny<T> extends true ? {
special: true;
type: 'any';
} : IsUnknown<T> extends true ? {
special: true;
type: 'unknown';
} : IsNever<T> extends true ? {
special: true;
type: 'never';
} : never;
export declare type PrintType<T> = IsUnknown<T> extends true ? 'unknown' : IsNever<T> extends true ? 'never' : IsAny<T> extends true ? never : boolean extends T ? 'boolean' : T extends boolean ? `literal boolean: ${T}` : T extends string ? string extends T ? 'string' : `literal string: ${T}` : T extends number ? number extends T ? 'number' : `literal number: ${T}` : T extends null ? 'null' : T extends undefined ? 'undefined' : T extends (...args: any[]) => any ? 'function' : '...';
export declare type MismatchInfo<Actual, Expected> = And<[Extends<PrintType<Actual>, '...'>, Not<IsAny<Actual>>]> extends true ? {
[K in keyof Actual | keyof Expected]: MismatchInfo<K extends keyof Actual ? Actual[K] : never, K extends keyof Expected ? Expected[K] : never>;
} : StrictEqualUsingBranding<Actual, Expected> extends true ? Actual : `Expected: ${PrintType<Expected>}, Actual: ${PrintType<Exclude<Actual, Expected>>}`;
/**

@@ -39,2 +53,3 @@ * Recursively walk a type and replace it with a branded type related to the original. This is useful for

this: DeepBrand<ThisParameterType<T>>;
props: DeepBrand<Omit<T, keyof Function>>;
} : T extends any[] ? {

@@ -68,8 +83,17 @@ type: 'array';

export declare type Extends<L, R> = IsNever<L> extends true ? IsNever<R> : [L] extends [R] ? true : false;
export declare type StrictExtends<L, R> = Extends<DeepBrand<L>, DeepBrand<R>>;
declare type StrictEqual<L, R> = (<T>() => T extends (L & T) | T ? true : false) extends <T>() => T extends (R & T) | T ? true : false ? IsNever<L> extends IsNever<R> ? true : false : false;
export declare type Equal<Left, Right, Branded = true> = Branded extends true ? And<[StrictExtends<Left, Right>, StrictExtends<Right, Left>]> : StrictEqual<Left, Right>;
export declare type ExtendsUsingBranding<L, R> = Extends<DeepBrand<L>, DeepBrand<R>>;
declare type StrictEqualUsingTSInternalIdenticalToOperator<L, R> = (<T>() => T extends (L & T) | T ? true : false) extends <T>() => T extends (R & T) | T ? true : false ? IsNever<L> extends IsNever<R> ? true : false : false;
export declare type StrictEqualUsingBranding<Left, Right> = And<[ExtendsUsingBranding<Left, Right>, ExtendsUsingBranding<Right, Left>]>;
export declare type HopefullyPerformantEqual<Left, Right> = StrictEqualUsingTSInternalIdenticalToOperator<Left, Right> extends true ? true : StrictEqualUsingBranding<Left, Right>;
export declare type Params<Actual> = Actual extends (...args: infer P) => any ? P : never;
export declare type ConstructorParams<Actual> = Actual extends new (...args: infer P) => any ? Actual extends new () => any ? P | [] : P : never;
declare type MismatchArgs<ActualResult extends boolean, ExpectedResult extends boolean> = Eq<ActualResult, ExpectedResult> extends true ? [] : [never];
declare const error: unique symbol;
declare type Mismatch = {
[error]: 'mismatch';
};
/** A type which should match anything passed as a value but *doesn't* match `Mismatch` - helps TypeScript select the right overload for `toEqualTypeOf` and `toMatchTypeOf`. */
declare type AValue = {
[error]?: undefined;
} | string | number | boolean | symbol | bigint | null | undefined | void;
declare type MismatchArgs<ActualResult extends boolean, ExpectedResult extends boolean> = Eq<ActualResult, ExpectedResult> extends true ? [] : [Mismatch];
export interface ExpectTypeOfOptions {

@@ -79,28 +103,125 @@ positive: boolean;

}
export interface ExpectTypeOf<Actual, Options extends ExpectTypeOfOptions> {
toBeAny: (...MISMATCH: MismatchArgs<IsAny<Actual>, Options['positive']>) => true;
toBeUnknown: (...MISMATCH: MismatchArgs<IsUnknown<Actual>, Options['positive']>) => true;
toBeNever: (...MISMATCH: MismatchArgs<IsNever<Actual>, Options['positive']>) => true;
toBeFunction: (...MISMATCH: MismatchArgs<Extends<Actual, (...args: any[]) => any>, Options['positive']>) => true;
toBeObject: (...MISMATCH: MismatchArgs<Extends<Actual, object>, Options['positive']>) => true;
toBeArray: (...MISMATCH: MismatchArgs<Extends<Actual, any[]>, Options['positive']>) => true;
toBeNumber: (...MISMATCH: MismatchArgs<Extends<Actual, number>, Options['positive']>) => true;
toBeString: (...MISMATCH: MismatchArgs<Extends<Actual, string>, Options['positive']>) => true;
toBeBoolean: (...MISMATCH: MismatchArgs<Extends<Actual, boolean>, Options['positive']>) => true;
toBeVoid: (...MISMATCH: MismatchArgs<Extends<Actual, void>, Options['positive']>) => true;
toBeSymbol: (...MISMATCH: MismatchArgs<Extends<Actual, symbol>, Options['positive']>) => true;
toBeNull: (...MISMATCH: MismatchArgs<Extends<Actual, null>, Options['positive']>) => true;
toBeUndefined: (...MISMATCH: MismatchArgs<Extends<Actual, undefined>, Options['positive']>) => true;
toBeNullable: (...MISMATCH: MismatchArgs<Not<Equal<Actual, NonNullable<Actual>, Options['branded']>>, Options['positive']>) => true;
declare type Inverted<T> = {
[error]: T;
};
declare type ExpectNull<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, null>;
};
declare type ExpectUndefined<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, undefined>;
};
declare type ExpectNumber<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, number>;
};
declare type ExpectString<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, string>;
};
declare type ExpectBoolean<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, boolean>;
};
declare type ExpectVoid<T> = {
[error]: T;
result: StrictEqualUsingTSInternalIdenticalToOperator<T, void>;
};
declare type ExpectFunction<T> = {
[error]: T;
result: Extends<T, (...args: any[]) => any>;
};
declare type ExpectObject<T> = {
[error]: T;
result: Extends<T, object>;
};
declare type ExpectArray<T> = {
[error]: T;
result: Extends<T, any[]>;
};
declare type ExpectSymbol<T> = {
[error]: T;
result: Extends<T, symbol>;
};
declare type ExpectAny<T> = {
[error]: T;
result: IsAny<T>;
};
declare type ExpectUnknown<T> = {
[error]: T;
result: IsUnknown<T>;
};
declare type ExpectNever<T> = {
[error]: T;
result: IsNever<T>;
};
declare type ExpectNullable<T> = {
[error]: T;
result: Not<StrictEqualUsingBranding<T, NonNullable<T>>>;
};
declare type Scolder<Expecter extends {
result: boolean;
}, Options extends {
positive: boolean;
}> = Expecter['result'] extends Options['positive'] ? () => true : Options['positive'] extends true ? Expecter : Inverted<Expecter>;
export interface PositiveExpectTypeOf<Actual> extends BaseExpectTypeOf<Actual, {
positive: true;
branded: false;
}> {
toEqualTypeOf: {
<Expected extends StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected> extends true ? unknown : MismatchInfo<Actual, Expected>>(value: Expected & AValue, // reason for `& AValue`: make sure this is only the selected overload when the end-user passes a value for an inferred typearg. The `Mismatch` type does match `AValue`.
...MISMATCH: MismatchArgs<StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected>, true>): true;
<Expected extends StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected> extends true ? unknown : MismatchInfo<Actual, Expected>>(...MISMATCH: MismatchArgs<StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected>, true>): true;
};
toMatchTypeOf: {
<Expected>(...MISMATCH: MismatchArgs<Extends<Actual, Expected>, Options['positive']>): true;
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Extends<Actual, Expected>, Options['positive']>): true;
<Expected extends Extends<Actual, Expected> extends true ? unknown : MismatchInfo<Actual, Expected>>(value: Expected & AValue, // reason for `& AValue`: make sure this is only the selected overload when the end-user passes a value for an inferred typearg. The `Mismatch` type does match `AValue`.
...MISMATCH: MismatchArgs<Extends<Actual, Expected>, true>): true;
<Expected extends Extends<Actual, Expected> extends true ? unknown : MismatchInfo<Actual, Expected>>(...MISMATCH: MismatchArgs<Extends<Actual, Expected>, true>): true;
};
toHaveProperty: <K extends keyof Actual>(key: K, ...MISMATCH: MismatchArgs<Extends<K, keyof Actual>, true>) => K extends keyof Actual ? PositiveExpectTypeOf<Actual[K]> : true;
not: NegativeExpectTypeOf<Actual>;
branded: {
toEqualTypeOf: <Expected extends StrictEqualUsingBranding<Actual, Expected> extends true ? unknown : MismatchInfo<Actual, Expected>>(...MISMATCH: MismatchArgs<StrictEqualUsingBranding<Actual, Expected>, true>) => true;
};
}
export interface NegativeExpectTypeOf<Actual> extends BaseExpectTypeOf<Actual, {
positive: false;
}> {
toEqualTypeOf: {
<Expected>(...MISMATCH: MismatchArgs<Equal<Actual, Expected, Options['branded']>, Options['positive']>): true;
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Equal<Actual, Expected, Options['branded']>, Options['positive']>): true;
<Expected>(value: Expected & AValue, ...MISMATCH: MismatchArgs<StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected>, false>): true;
<Expected>(...MISMATCH: MismatchArgs<StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected>, false>): true;
};
toMatchTypeOf: {
<Expected>(value: Expected & AValue, // reason for `& AValue`: make sure this is only the selected overload when the end-user passes a value for an inferred typearg. The `Mismatch` type does match `AValue`.
...MISMATCH: MismatchArgs<Extends<Actual, Expected>, false>): true;
<Expected>(...MISMATCH: MismatchArgs<Extends<Actual, Expected>, false>): true;
};
toHaveProperty: <K extends string | number | symbol>(key: K, ...MISMATCH: MismatchArgs<Extends<K, keyof Actual>, false>) => true;
branded: {
toEqualTypeOf: <Expected>(...MISMATCH: MismatchArgs<StrictEqualUsingTSInternalIdenticalToOperator<Actual, Expected>, false>) => true;
};
}
export declare type ExpectTypeOf<Actual, Options extends {
positive: boolean;
}> = (Options['positive'] extends true ? PositiveExpectTypeOf<Actual> : NegativeExpectTypeOf<Actual>);
export interface BaseExpectTypeOf<Actual, Options extends {
positive: boolean;
}> {
toBeAny: Scolder<ExpectAny<Actual>, Options>;
toBeUnknown: Scolder<ExpectUnknown<Actual>, Options>;
toBeNever: Scolder<ExpectNever<Actual>, Options>;
toBeFunction: Scolder<ExpectFunction<Actual>, Options>;
toBeObject: Scolder<ExpectObject<Actual>, Options>;
toBeArray: Scolder<ExpectArray<Actual>, Options>;
toBeNumber: Scolder<ExpectNumber<Actual>, Options>;
toBeString: Scolder<ExpectString<Actual>, Options>;
toBeBoolean: Scolder<ExpectBoolean<Actual>, Options>;
toBeVoid: Scolder<ExpectVoid<Actual>, Options>;
toBeSymbol: Scolder<ExpectSymbol<Actual>, Options>;
toBeNull: Scolder<ExpectNull<Actual>, Options>;
toBeUndefined: Scolder<ExpectUndefined<Actual>, Options>;
toBeNullable: Scolder<ExpectNullable<Actual>, Options>;
toBeCallableWith: Options['positive'] extends true ? (...args: Params<Actual>) => true : never;
toBeConstructibleWith: Options['positive'] extends true ? (...args: ConstructorParams<Actual>) => true : never;
toHaveProperty: <K extends string>(key: K, ...MISMATCH: MismatchArgs<Extends<K, keyof Actual>, Options['positive']>) => K extends keyof Actual ? ExpectTypeOf<Actual[K], Options> : true;
extract: <V>(v?: V) => ExpectTypeOf<Extract<Actual, V>, Options>;

@@ -118,10 +239,2 @@ exclude: <V>(v?: V) => ExpectTypeOf<Exclude<Actual, V>, Options>;

asserts: Actual extends (v: any, ...args: any[]) => asserts v is infer T ? unknown extends T ? never : ExpectTypeOf<T, Options> : never;
branded: Omit<ExpectTypeOf<Actual, {
positive: Options['positive'];
branded: true;
}>, 'branded'>;
not: Omit<ExpectTypeOf<Actual, {
positive: Not<Options['positive']>;
branded: Options['branded'];
}>, 'not'>;
}

@@ -128,0 +241,0 @@ export declare type _ExpectTypeOf = {

@@ -5,2 +5,3 @@ "use strict";

const secret = Symbol('secret');
const error = Symbol('error');
const fn = () => true;

@@ -7,0 +8,0 @@ /**

4

package.json
{
"name": "expect-type",
"version": "0.16.0",
"version": "0.17.0-1",
"engines": {

@@ -43,3 +43,3 @@ "node": ">=12.0.0"

"jest": "28.1.3",
"np": "8.0.1",
"np": "^8.0.4",
"strip-ansi": "6.0.1",

@@ -46,0 +46,0 @@ "ts-jest": "28.0.8",

@@ -35,2 +35,5 @@ # expect-type

- [Features](#features)
- [Where is `.toExtend`?](#where-is-toextend)
- [Use internal type helpers at your own risk](#use-internal-type-helpers-at-your-own-risk)
- [Error messages](#error-messages)
- [Within test frameworks](#within-test-frameworks)

@@ -85,8 +88,17 @@ - [Jest & `eslint-plugin-jest`](#jest--eslint-plugin-jest)

To allow for extra properties, use `.toMatchTypeOf`. This checks that an object "matches" a type. This is similar to jest's `.toMatchObject`:
To allow for extra properties, use `.toMatchTypeOf`. This is roughly equivalent to an `extends` constraint in a function type argument.:
```typescript
expectTypeOf({a: 1, b: 1}).toMatchTypeOf({a: 1})
expectTypeOf({a: 1, b: 1}).toMatchTypeOf<{a: number}>()
```
`.toEqualTypeOf` and `.toMatchTypeOf` both fail on missing properties:
```typescript
// @ts-expect-error
expectTypeOf({a: 1}).toEqualTypeOf<{a: number; b: number}>()
// @ts-expect-error
expectTypeOf({a: 1}).toMatchTypeOf<{a: number; b: number}>()
```
Another example of the difference between `.toMatchTypeOf` and `.toEqualTypeOf`, using generics. `.toMatchTypeOf` can be used for "is-a" relationships:

@@ -285,2 +297,11 @@

You can't use `.toBeCallableWith` with `.not` - you need to use ts-expect-error::
```typescript
const f = (a: number) => [a, a]
// @ts-expect-error
expectTypeOf(f).toBeCallableWith('foo')
```
You can also check type guards & type assertions:

@@ -441,3 +462,3 @@

To workaround, you can use a mapped type:
To workaround for simple cases, you can use a mapped type:

@@ -449,8 +470,92 @@ ```typescript

```
But this won't work if the nesting is deeper in the type. For these situations, you can use the `.branded` helper. Note that this comes at a performance cost, and can cause the compiler to 'give up' if used with excessively deep types, so use sparingly. This helper is under `.branded` because it depply transforms the Actual and Expected types into a pseudo-AST:
```typescript
// @ts-expect-error
expectTypeOf<{a: {b: 1} & {c: 1}}>().toEqualTypeOf<{a: {b: 1; c: 1}}>()
expectTypeOf<{a: {b: 1} & {c: 1}}>().branded.toEqualTypeOf<{a: {b: 1; c: 1}}>()
```
Be careful with `.branded` for very deep or complex types, though. If possible you should find a way to simplify your test to avoid needing to use it:
```typescript
// This *should* result in an error, but the "branding" mechanism produces too large a type and TypeScript just gives up! https://github.com/microsoft/TypeScript/issues/50670
expectTypeOf<() => () => () => () => 1>().branded.toEqualTypeOf<() => () => () => () => 2>()
// @ts-expect-error the non-branded implementation catches the error as expected.
expectTypeOf<() => () => () => () => 1>().toEqualTypeOf<() => () => () => () => 2>()
```
So, if you have an extremely deep type which ALSO has an intersection in it, you're out of luck and this library won't be able to test your type properly:
```typescript
// @ts-expect-error this fails, but it should succeed.
expectTypeOf<() => () => () => () => {a: 1} & {b: 2}>().toEqualTypeOf<
() => () => () => () => {a: 1; b: 2}
>()
// this succeeds, but it should fail.
expectTypeOf<() => () => () => () => {a: 1} & {b: 2}>().branded.toEqualTypeOf<
() => () => () => () => {a: 1; c: 2}
>()
```
Another limitation: passing `this` references to `expectTypeOf` results in errors.:
```typescript
class B {
b = 'b'
foo() {
// @ts-expect-error
expectTypeOf(this).toEqualTypeOf(this)
// @ts-expect-error
expectTypeOf(this).toMatchTypeOf(this)
}
}
// Instead of the above, try something like this:
expectTypeOf(B).instance.toEqualTypeOf<{b: string; foo: () => void}>()
```
<!-- codegen:end -->
### Where is `.toExtend`?
A few people have asked for a method like `toExtend` - this is essentially what `toMatchTypeOf` is. There are some cases where it doesn't _precisely_ match the `extends` operator in TypeScript, but for most practical use cases, you can think of this as the same thing.
### Use internal type helpers at your own risk
This library also exports some helper types for performing boolean operations on types, checking extension/equality in various ways, branding types, and checking for various special types like `never`, `any`, `unknown`. Nothing is stopping you using these beyond the following warning:
>All internal types that are not documented here are _not_ part of the supported API surface, and may be renamed, modified, or removed, without warning or documentation in release notes.
For a dedicated internal type library, feel free to look at the [source code](./src/index.ts) for inspiration - or better, use a library like [type-fest](https://npmjs.com/package/type-fest).
### Error messages
When types don't match, `.toEqualTypeOf` and `toMatchTypeOf` use a special helper type to produce error messages that are as actionable as possible. But there's a bit of an nuance to understanding them. Since the assertions are written "fluently", the failure should be on the "expected" type, not the "actual" type (`expect<Actual>().toEqualTypeOf<Expected>()`). This means that type errors can be a little confusing - so this library produces a `MismatchInfo` type to try to make explicit what the expectation is. For example:
```ts
expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>()
```
Is an assertion that will fail, since `{a: 1}` has type `{a: number}` and not `{a: string}`. The error message in this case will read something like this:
```
test/test.ts:9:99 - error TS2344: Type '{ a: string; }' does not satisfy the constraint '{ a: \\"Expected: string, Actual: number\\"; }'.
Types of property 'a' are incompatible.
Type 'string' is not assignable to type '\\"Expected: string, Actual: number\\"'.
9 expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>()
~~~~~~~~~~~
```
Not that the type constraint reported is a human-readable messaging specifying both the "expected" and "actual" types. Rather than taking the sentence `Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"` literally - just look at the property name (`'a'`) and the message: `Expected: string, Actual: number`. This will tell you what's wrong, in most cases. Extremely complex types will of course be more effort to debug, and may require some experimentation. Please [raise an issue](https://github.com/mmkal/expect-type) if the error messages are actually misleading.
### Within test frameworks
#### Jest & `eslint-plugin-jest`
If you're using Jest along with `eslint-plugin-jest`, you will get warnings from the [`jest/expect-expect`](https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/expect-expect.md) rule, complaining that "Test has no assertions" for tests that only use `expectTypeOf()`.
If you're using Jest along with `eslint-plugin-jest`, you may get warnings from the [`jest/expect-expect`](https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/expect-expect.md) rule, complaining that "Test has no assertions" for tests that only use `expectTypeOf()`.

@@ -457,0 +562,0 @@ To remove this warning, configure the ESlint rule to consider `expectTypeOf` as an assertion:

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc