Skip to content

Commit

Permalink
🎉 Add new package @fast-check/expect-type (#3348)
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz authored Oct 23, 2022
1 parent 844fe0a commit b6f68fc
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 171 deletions.
2 changes: 2 additions & 0 deletions .yarn/versions/5fd3115f.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
releases:
"@fast-check/expect-type": patch
21 changes: 21 additions & 0 deletions packages/expect-type/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 Nicolas DUBIEN

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
21 changes: 21 additions & 0 deletions packages/expect-type/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# `@fast-check/expect-type`

Make sure your types are the ones you expect (similar to `tsd`)

<a href="https://badge.fury.io/js/@fast-check%2Fexpect-type"><img src="https://badge.fury.io/js/@fast-check%2Fexpect-type.svg" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/@fast-check/poisoning"><img src="https://img.shields.io/npm/dm/@fast-check%2Fexpect-type" alt="monthly downloads" /></a>
<a href="https://github.com/dubzzz/fast-check/blob/main/packages/poisoning/LICENSE"><img src="https://img.shields.io/npm/l/@fast-check%2Fexpect-type.svg" alt="License" /></a>

---

## Easy to use

This package performs compilation time checks only. Running the check functions at runtime is a no-op.

```ts
import { expectType, expectTypeAssignable } from '@fast-check/expect-type';
// your code or you own imports

expectType<number>()(f(1, 2), 'expect the output of f when passed 1 and 2 to be number');
expectTypeAssignable<number>()(f(1, 2), 'expect the output of f when passed 1 and 2 to be assignable to number');
```
55 changes: 55 additions & 0 deletions packages/expect-type/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@fast-check/expect-type",
"description": "Make sure your types are the ones you expect (similar to tsd)",
"version": "0.0.0",
"type": "commonjs",
"main": "src/main.js",
"exports": {
"./package.json": "./package.json",
".": {
"require": "./src/main.js",
"import": "./src/esm/main.js",
"default": "./src/esm/main.js"
}
},
"module": "src/esm/main.js",
"types": "src/main.d.ts",
"files": [
"src"
],
"scripts": {
"test": "tsc --noEmit",
"typecheck": "tsc --noEmit"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dubzzz/fast-check.git",
"directory": "packages/expect-type"
},
"author": "Nicolas DUBIEN <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/dubzzz/fast-check/issues"
},
"homepage": "https://github.com/dubzzz/fast-check/tree/main/packages/expect-type#readme",
"devDependencies": {
"typescript": "^4.8.4"
},
"keywords": [
"type",
"checker",
"tsd",
"assert",
"typescript"
],
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
},
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
}
]
}
8 changes: 8 additions & 0 deletions packages/expect-type/src/esm/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-empty-function */
export function expectType() {
return function () {};
}
export function expectTypeAssignable() {
return function () {};
}
3 changes: 3 additions & 0 deletions packages/expect-type/src/esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
24 changes: 24 additions & 0 deletions packages/expect-type/src/internals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export declare type Not<T> = T extends true ? false : true;
export declare type And<T, U> = T extends true ? (U extends true ? true : false) : false;
export declare type Or<T, U> = T extends false ? (U extends false ? false : true) : true;
export declare type IsNever<T> = [T] extends [never] ? true : false;
export declare type Extends<T, U> = T extends U ? true : false;
export declare type ExtendsString<T> = Extends<T, string> extends boolean
? boolean extends Extends<T, string>
? true
: false
: false; // Extends<T, string> is: false for unknown but boolean for any
export declare type IsUnknown<T> = And<
And<Not<IsNever<T>>, Extends<T, unknown>>,
And<Extends<unknown, T>, Not<ExtendsString<T>>>
>;
export declare type IsAny<T> = And<
And<Not<IsNever<T>>, Not<IsUnknown<T>>>,
And<Extends<T, any>, Extends<any, T> extends true ? true : false>
>;
export declare type IsSame<T, U> = [T, U] extends [U, T]
? Or<
Or<And<IsAny<T>, IsAny<U>>, And<IsUnknown<T>, IsUnknown<U>>>,
And<And<Not<IsAny<T>>, Not<IsAny<U>>>, And<Not<IsUnknown<T>>, Not<IsUnknown<U>>>>
>
: false;
9 changes: 9 additions & 0 deletions packages/expect-type/src/main.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IsSame, Extends } from './internals';
export declare function expectType<TExpectedType>(): <TReal>(
arg: TReal,
...noArgs: IsSame<TExpectedType, TReal> extends true ? [string] : [{ expected: TExpectedType; got: TReal }]
) => void;
export declare function expectTypeAssignable<TExpectedType>(): <TReal>(
arg: TReal,
...noArgs: Extends<TReal, TExpectedType> extends true ? [string] : [{ expected: TExpectedType; got: TReal }]
) => void;
10 changes: 10 additions & 0 deletions packages/expect-type/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-empty-function */
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.expectType = function expectType() {
return function () {};
};
exports.expectTypeAssignable = function expectTypeAssignable() {
return function () {};
};
54 changes: 54 additions & 0 deletions packages/expect-type/test/internals.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Not, And, Or, IsNever, IsUnknown, IsAny, IsSame } from '../src/internals';

export const Test_Not_true: Not<true> = false;
export const Test_Not_false: Not<false> = true;

export const Test_And_false_false: And<false, false> = false;
export const Test_And_false_true: And<false, true> = false;
export const Test_And_true_false: And<true, false> = false;
export const Test_And_true_true: And<true, true> = true;

export const Test_Or_false_false: Or<false, false> = false;
export const Test_Or_false_true: Or<false, true> = true;
export const Test_Or_true_false: Or<true, false> = true;
export const Test_Or_true_true: Or<true, true> = true;

export const Test_IsNever_null: IsNever<null> = false;
export const Test_IsNever_undefined: IsNever<undefined> = false;
export const Test_IsNever_number: IsNever<number> = false;
export const Test_IsNever_any: IsNever<any> = false;
export const Test_IsNever_unknown: IsNever<unknown> = false;
export const Test_IsNever_never: IsNever<never> = true;

export const Test_IsUnknown_null: IsUnknown<null> = false;
export const Test_IsUnknown_undefined: IsUnknown<undefined> = false;
export const Test_IsUnknown_number: IsUnknown<number> = false;
export const Test_IsUnknown_any: IsUnknown<any> = false;
export const Test_IsUnknown_unknown: IsUnknown<unknown> = true;
export const Test_IsUnknown_never: IsUnknown<never> = false;

export const Test_IsAny_null: IsAny<null> = false;
export const Test_IsAny_undefined: IsAny<undefined> = false;
export const Test_IsAny_number: IsAny<number> = false;
export const Test_IsAny_any: IsAny<any> = true;
export const Test_IsAny_unknown: IsAny<unknown> = false;
export const Test_IsAny_never: IsAny<never> = false;

export const Test_IsSame_true_true: IsSame<true, true> = true;
export const Test_IsSame_true_false: IsSame<true, false> = false;
export const Test_IsSame_true_boolean: IsSame<true, boolean> = false;
export const Test_IsSame_never_boolean: IsSame<never, boolean> = false;
export const Test_IsSame_never_any: IsSame<never, any> = false;
export const Test_IsSame_never_unknown: IsSame<never, unknown> = false;
export const Test_IsSame_never_never: IsSame<never, never> = true;
export const Test_IsSame_any_boolean: IsSame<any, boolean> = false;
export const Test_IsSame_any_any: IsSame<any, any> = true;
export const Test_IsSame_any_any_Bis: IsSame<IsSame<any, any>, true> = true;
export const Test_IsSame_any_unknown: IsSame<any, unknown> = false;
export const Test_IsSame_any_never: IsSame<any, never> = false;
export const Test_IsSame_unknown_boolean: IsSame<unknown, boolean> = false;
export const Test_IsSame_unknown_any: IsSame<unknown, any> = false;
export const Test_IsSame_unknown_unknown: IsSame<unknown, unknown> = true;
export const Test_IsSame_unknown_never: IsSame<unknown, never> = false;
export const Test_IsSame_tuple_number_tuple_string: IsSame<[number], [string]> = false;
//export const Test_IsSame_tuple_any_tuple_unknown: IsSame<[any], [unknown]> = false;
78 changes: 78 additions & 0 deletions packages/expect-type/test/main.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* eslint-disable @typescript-eslint/ban-types */
import { expectType, expectTypeAssignable } from '../src/main';

function type<T>() {
return null as any as T;
}

// expectType

expectType<5>()(type<5>(), '5 is 5');
expectType<number>()(type<number>(), 'number is number');
expectType<never>()(type<never>(), 'never is never');
expectType<any>()(type<any>(), 'any is any');
expectType<unknown>()(type<unknown>(), 'unknown is unknown');
expectType<{ a: number }>()(type<{ a: number }>(), '{a:number} is {a:number}');
expectType<Promise<5>>()(type<Promise<5>>(), 'Promise<5> is Promise<5>');
expectType<[5]>()(type<[5]>(), '[5] is [5]');
expectType<[any]>()(type<[any]>(), '[any] is [any]');

// @ts-expect-error - 5 is not number
expectType<number>()(type<5>(), '5 is not number');
// @ts-expect-error - number is not 5
expectType<5>()(type<number>(), 'number is not 5');
// @ts-expect-error - number is not never
expectType<never>()(type<number>(), 'number is not never');
// @ts-expect-error - any is not never
expectType<never>()(type<any>(), 'any is not never');
// @ts-expect-error - unknown is not never
expectType<never>()(type<unknown>(), 'unknown is not never');
// @ts-expect-error - number is not any
expectType<any>()(type<number>(), 'number is not any');
// @ts-expect-error - never is not any
expectType<any>()(type<never>(), 'never is not any');
// @ts-expect-error - unknown is not any
expectType<any>()(type<unknown>(), 'unknown is not any');
// @ts-expect-error - number is not unknown
expectType<unknown>()(type<number>(), 'number is not unknown');
// @ts-expect-error - never is not unknown
expectType<unknown>()(type<never>(), 'never is not unknown');
// @ts-expect-error - any is not unknown
expectType<unknown>()(type<any>(), 'any is not unknown');
// @ts-expect-error - {a?:number} is not {a:number}
expectType<{ a: number }>()(type<{ a?: number }>(), '{a?:number} is not {a:number}');
// @ts-expect-error - {a:number} is not {a?:number}
expectType<{ a?: number }>()(type<{ a: number }>(), '{a:number} is not {a?:number}');
// @ts-expect-error - {a:number} is not {}
expectType<{}>()(type<{ a: number }>(), '{a:number} is not {}');
// @ts-expect-error - Promise<number> is not Promise<5>
expectType<Promise<5>>()(type<Promise<number>>(), 'Promise<number> is not Promise<5>');
// @ts-expect-error - Promise<5> is not Promise<number>
expectType<Promise<number>>()(type<Promise<5>>(), 'Promise<5> is not Promise<number>');
// @ts-expect-error - [15] is not [5]
expectType<[5]>()(type<[15]>(), '[15] is not [5]');
//// @ts-expect-error - [unknown] is not [any]
//expectType<[any]>()(type<[unknown]>(), '[unknown] is not [any]');
//// @ts-expect-error - [any] is not [unknown]
//expectType<[unknown]>()(type<[any]>(), '[any] is not [unknown]');

// expectTypeAssignable

expectTypeAssignable<number>()(type<number>(), 'number is assignable to number');
expectTypeAssignable<number>()(type<5>(), '5 is assignable to number');
expectTypeAssignable<3 | 5>()(type<5>(), '5 is assignable to 3|5');
expectTypeAssignable<unknown>()(type<number>(), 'number is assignable to unknown');
expectTypeAssignable<any>()(type<number>(), 'number is assignable to any');
expectTypeAssignable<{ a?: number }>()(type<{ a: number }>(), '{a:number} is assignable to {a?:number}');
expectTypeAssignable<{}>()(type<{ a: number }>(), '{a:number} is assignable to {}');

// @ts-expect-error - number is not assignable to 5
expectTypeAssignable<5>()(type<number>(), 'number is not assignable to 5');
// @ts-expect-error - 3|5 is not assignable to 5
expectTypeAssignable<5>()(type<3 | 5>(), '3|5 is not assignable to 5');
// @ts-expect-error - {a?:number} is not assignable to {a:number}
expectTypeAssignable<{ a: number }>()(type<{ a?: number }>(), '{a?:number} is not assignable to {a:number}');
// @ts-expect-error - unknown is not assignable to number
expectTypeAssignable<number>()(type<unknown>(), 'unknown is not assignable to number');
// @ts-expect-error - unknown is not assignable to never
expectTypeAssignable<never>()(type<unknown>(), 'unknown is not assignable to never');
3 changes: 3 additions & 0 deletions packages/expect-type/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.common.json"
}
2 changes: 1 addition & 1 deletion packages/test-types/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/* eslint-disable @typescript-eslint/no-unused-vars */
import fc from 'fast-check';
import { expectType, expectTypeAssignable } from './type-checker';
import { expectType, expectTypeAssignable } from '@fast-check/expect-type';

// assert
expectType<void>()(fc.assert(fc.property(fc.nat(), () => {})), 'Synchronous property means synchronous assert');
Expand Down
1 change: 1 addition & 0 deletions packages/test-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"test": "tsc"
},
"dependencies": {
"@fast-check/expect-type": "workspace:*",
"fast-check": "workspace:*"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit b6f68fc

Please sign in to comment.