expect-type
Advanced tools
Comparing version 0.15.0 to 0.16.0
@@ -37,2 +37,3 @@ export declare type Not<T extends boolean> = T extends true ? false : true; | ||
return: DeepBrand<R>; | ||
this: DeepBrand<ThisParameterType<T>>; | ||
} : T extends any[] ? { | ||
@@ -67,48 +68,67 @@ type: 'array'; | ||
export declare type StrictExtends<L, R> = Extends<DeepBrand<L>, DeepBrand<R>>; | ||
export declare type Equal<Left, Right> = And<[StrictExtends<Left, Right>, StrictExtends<Right, Left>]>; | ||
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 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<B extends boolean, C extends boolean> = Eq<B, C> extends true ? [] : [never]; | ||
export interface ExpectTypeOf<Actual, B extends boolean> { | ||
toBeAny: (...MISMATCH: MismatchArgs<IsAny<Actual>, B>) => true; | ||
toBeUnknown: (...MISMATCH: MismatchArgs<IsUnknown<Actual>, B>) => true; | ||
toBeNever: (...MISMATCH: MismatchArgs<IsNever<Actual>, B>) => true; | ||
toBeFunction: (...MISMATCH: MismatchArgs<Extends<Actual, (...args: any[]) => any>, B>) => true; | ||
toBeObject: (...MISMATCH: MismatchArgs<Extends<Actual, object>, B>) => true; | ||
toBeArray: (...MISMATCH: MismatchArgs<Extends<Actual, any[]>, B>) => true; | ||
toBeNumber: (...MISMATCH: MismatchArgs<Extends<Actual, number>, B>) => true; | ||
toBeString: (...MISMATCH: MismatchArgs<Extends<Actual, string>, B>) => true; | ||
toBeBoolean: (...MISMATCH: MismatchArgs<Extends<Actual, boolean>, B>) => true; | ||
toBeVoid: (...MISMATCH: MismatchArgs<Extends<Actual, void>, B>) => true; | ||
toBeSymbol: (...MISMATCH: MismatchArgs<Extends<Actual, symbol>, B>) => true; | ||
toBeNull: (...MISMATCH: MismatchArgs<Extends<Actual, null>, B>) => true; | ||
toBeUndefined: (...MISMATCH: MismatchArgs<Extends<Actual, undefined>, B>) => true; | ||
toBeNullable: (...MISMATCH: MismatchArgs<Not<Equal<Actual, NonNullable<Actual>>>, B>) => true; | ||
declare type MismatchArgs<ActualResult extends boolean, ExpectedResult extends boolean> = Eq<ActualResult, ExpectedResult> extends true ? [] : [never]; | ||
export interface ExpectTypeOfOptions { | ||
positive: boolean; | ||
branded: 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; | ||
toMatchTypeOf: { | ||
<Expected>(...MISMATCH: MismatchArgs<Extends<Actual, Expected>, B>): true; | ||
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Extends<Actual, Expected>, B>): true; | ||
<Expected>(...MISMATCH: MismatchArgs<Extends<Actual, Expected>, Options['positive']>): true; | ||
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Extends<Actual, Expected>, Options['positive']>): true; | ||
}; | ||
toEqualTypeOf: { | ||
<Expected>(...MISMATCH: MismatchArgs<Equal<Actual, Expected>, B>): true; | ||
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Equal<Actual, Expected>, B>): true; | ||
<Expected>(...MISMATCH: MismatchArgs<Equal<Actual, Expected, Options['branded']>, Options['positive']>): true; | ||
<Expected>(expected: Expected, ...MISMATCH: MismatchArgs<Equal<Actual, Expected, Options['branded']>, Options['positive']>): true; | ||
}; | ||
toBeCallableWith: B extends true ? (...args: Params<Actual>) => true : never; | ||
toBeConstructibleWith: B extends true ? (...args: ConstructorParams<Actual>) => true : never; | ||
toHaveProperty: <K extends string>(key: K, ...MISMATCH: MismatchArgs<Extends<K, keyof Actual>, B>) => K extends keyof Actual ? ExpectTypeOf<Actual[K], B> : true; | ||
extract: <V>(v?: V) => ExpectTypeOf<Extract<Actual, V>, B>; | ||
exclude: <V>(v?: V) => ExpectTypeOf<Exclude<Actual, V>, B>; | ||
parameter: <K extends keyof Params<Actual>>(number: K) => ExpectTypeOf<Params<Actual>[K], B>; | ||
parameters: ExpectTypeOf<Params<Actual>, B>; | ||
constructorParameters: ExpectTypeOf<ConstructorParams<Actual>, B>; | ||
instance: Actual extends new (...args: any[]) => infer I ? ExpectTypeOf<I, B> : never; | ||
returns: Actual extends (...args: any[]) => infer R ? ExpectTypeOf<R, B> : never; | ||
resolves: Actual extends PromiseLike<infer R> ? ExpectTypeOf<R, B> : never; | ||
items: Actual extends ArrayLike<infer R> ? ExpectTypeOf<R, B> : never; | ||
guards: Actual extends (v: any, ...args: any[]) => v is infer T ? ExpectTypeOf<T, B> : never; | ||
asserts: Actual extends (v: any, ...args: any[]) => asserts v is infer T ? unknown extends T ? never : ExpectTypeOf<T, B> : never; | ||
not: ExpectTypeOf<Actual, Not<B>>; | ||
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>; | ||
exclude: <V>(v?: V) => ExpectTypeOf<Exclude<Actual, V>, Options>; | ||
parameter: <K extends keyof Params<Actual>>(number: K) => ExpectTypeOf<Params<Actual>[K], Options>; | ||
parameters: ExpectTypeOf<Params<Actual>, Options>; | ||
constructorParameters: ExpectTypeOf<ConstructorParams<Actual>, Options>; | ||
thisParameter: ExpectTypeOf<ThisParameterType<Actual>, Options>; | ||
instance: Actual extends new (...args: any[]) => infer I ? ExpectTypeOf<I, Options> : never; | ||
returns: Actual extends (...args: any[]) => infer R ? ExpectTypeOf<R, Options> : never; | ||
resolves: Actual extends PromiseLike<infer R> ? ExpectTypeOf<R, Options> : never; | ||
items: Actual extends ArrayLike<infer R> ? ExpectTypeOf<R, Options> : never; | ||
guards: Actual extends (v: any, ...args: any[]) => v is infer T ? ExpectTypeOf<T, Options> : never; | ||
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'>; | ||
} | ||
export declare type _ExpectTypeOf = { | ||
<Actual>(actual: Actual): ExpectTypeOf<Actual, true>; | ||
<Actual>(): ExpectTypeOf<Actual, true>; | ||
<Actual>(actual: Actual): ExpectTypeOf<Actual, { | ||
positive: true; | ||
branded: false; | ||
}>; | ||
<Actual>(): ExpectTypeOf<Actual, { | ||
positive: true; | ||
branded: false; | ||
}>; | ||
}; | ||
@@ -115,0 +135,0 @@ /** |
@@ -36,5 +36,7 @@ "use strict"; | ||
'constructorParameters', | ||
'thisParameter', | ||
'instance', | ||
'guards', | ||
'asserts', | ||
'branded', | ||
]; | ||
@@ -41,0 +43,0 @@ const obj = { |
{ | ||
"name": "expect-type", | ||
"version": "0.15.0", | ||
"version": "0.16.0", | ||
"engines": { | ||
"node": ">=12.0.0" | ||
}, | ||
"keywords": [ | ||
@@ -36,9 +39,12 @@ "typescript", | ||
"@types/jest": "29.0.0", | ||
"@types/node": "^14.0.0", | ||
"eslint": "8.23.0", | ||
"eslint-plugin-mmkal": "0.0.1-2", | ||
"jest": "28.1.3", | ||
"np": "7.6.2", | ||
"np": "8.0.1", | ||
"strip-ansi": "6.0.1", | ||
"ts-jest": "28.0.8", | ||
"ts-morph": "16.0.0", | ||
"typescript": "4.8.2" | ||
} | ||
} |
@@ -39,2 +39,3 @@ # expect-type | ||
- [Comparison](#comparison) | ||
- [Contributing](#contributing) | ||
<!-- codegen:end --> | ||
@@ -58,3 +59,3 @@ | ||
<!-- codegen:start {preset: markdownFromTests, source: test/index.test.ts} --> | ||
<!-- codegen:start {preset: markdownFromTests, source: test/usage.test.ts} --> | ||
Check an object's type with `.toEqualTypeOf`: | ||
@@ -310,2 +311,26 @@ | ||
Check function `this` parameters: | ||
```typescript | ||
function greet(this: {name: string}, message: string) { | ||
return `Hello ${this.name}, here's your message: ${message}` | ||
} | ||
expectTypeOf(greet).thisParameter.toEqualTypeOf<{name: string}>() | ||
``` | ||
Distinguish between functions with different `this` parameters: | ||
```typescript | ||
function greetFormal(this: {title: string; name: string}, message: string) { | ||
return `Dear ${this.title} ${this.name}, here's your message: ${message}` | ||
} | ||
function greetCasual(this: {name: string}, message: string) { | ||
return `Hi ${this.name}, here's your message: ${message}` | ||
} | ||
expectTypeOf(greetFormal).not.toEqualTypeOf(greetCasual) | ||
``` | ||
Class instance types: | ||
@@ -332,2 +357,8 @@ | ||
You can also compare arrays directly: | ||
```typescript | ||
expectTypeOf<any[]>().not.toEqualTypeOf<number[]>() | ||
``` | ||
Check that functions never return: | ||
@@ -402,2 +433,18 @@ | ||
``` | ||
Known limitation: Intersection types can cause issues with `toEqualTypeOf`: | ||
```typescript | ||
// @ts-expect-error the following line doesn't compile, even though the types are arguably the same. | ||
// See https://github.com/mmkal/expect-type/pull/21 | ||
expectTypeOf<{a: 1} & {b: 2}>().toEqualTypeOf<{a: 1; b: 2}>() | ||
``` | ||
To workaround, you can use a mapped type: | ||
```typescript | ||
type Simplify<T> = {[K in keyof T]: T[K]} | ||
expectTypeOf<Simplify<{a: 1} & {b: 2}>>().toEqualTypeOf<{a: 1; b: 2}>() | ||
``` | ||
<!-- codegen:end --> | ||
@@ -457,1 +504,11 @@ | ||
- small implementation with no dependencies. <200 lines of code - [take a look!](./src/index.ts) (tsd, for comparison, is [2.6MB](https://bundlephobia.com/result?p=tsd@0.13.1) because it ships a patched version of typescript). | ||
## Contributing | ||
In most cases, it's worth checking existing issues or creating on to discuss a new feature or a bug fix before opening a pull request. | ||
Once you're ready to make a pull request: clone the repo, and install pnpm if you don't have it already with `npm install --global pnpm`. Lockfiles for `npm` and `yarn` are gitignored. | ||
If you're adding a feature, you should write a self-contained usage example in the form of a test, in [test/usage.test.ts](./test/usage.test.ts). This file is used to populate the bulk of this readme using [eslint-plugin-codegen](https://npmjs.com/package/eslint-plugin-codegen), and to generate an ["errors" test file](./test/errors.test.ts), which captures the error messages that are emitted for failing assertions by the typescript compiler. So, the test name should be written as a human-readable sentence explaining the usage example. Have a look at the existing tests for an idea of the style. | ||
After adding the tests, run `npm run lint -- --fix` to update the readme, and `npm test -- --updateSnapshot` to update the errors test. The generated documentation and tests should be pushed to the same branch as the source code, and submitted as a pull request. CI will test that the docs and tests are up to date if you forget to run these commands. |
39947
228
509
10