Skip to content

Commit

Permalink
Improve assert utils
Browse files Browse the repository at this point in the history
Also removed broken `IfIdenticalInternalTSRepresentation` type.
  • Loading branch information
papb committed Jun 5, 2021
1 parent cc1c6bc commit f196dc6
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 31 deletions.
18 changes: 10 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ on: [push, pull_request]

jobs:
test:
strategy:
fail-fast: false
matrix:
ts-version: ['4.1', '4.2', '4.3']
name: TypeScript ${{ matrix.ts-version }}
runs-on: ubuntu-latest
steps:
-
uses: actions/checkout@v2
-
name: Use Node.js 12.x
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
-
run: npm install
-
run: npm test
- run: npm install
- run: npm install --save-dev typescript@~${{ matrix.ts-version }}
- run: npm test
18 changes: 9 additions & 9 deletions .xo-config.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"rules": {
"unicorn/prevent-abbreviations": "off",
"no-multiple-empty-lines": "off",
"linebreak-style": ["error", "unix"],
"object-curly-spacing": ["error", "always"],
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/indent": "off",
"unicorn/prevent-abbreviations": "off",
"linebreak-style": [
"error",
"unix"
],
"object-curly-spacing": [
"error",
"always"
]
"@typescript-eslint/object-curly-spacing": ["error", "always"],
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/no-confusing-void-expression": "off"
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
},
"devDependencies": {
"del-cli": "^3.0.1",
"eslint-config-xo-typescript": "^0.41.1",
"np": "^6.5.0",
"typescript": "~4.1.3",
"xo": "^0.36.1"
"xo": "^0.39.1"
},
"publishConfig": {
"access": "public"
Expand Down
21 changes: 19 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@ $ npm install @papb/assorted-ts-utils
## Usage

```ts
import { tsAssertTrue } from '@papb/assorted-ts-utils/assert';
import {
StrictEqual,
ReplaceKeyType,
ParseArray,
ReplaceAllTypeOccurrences
ReplaceAllTypeOccurrences,
IfAny,
IfNever
} from '@papb/assorted-ts-utils';

import {
tsAssertTrue,
tsAssertTypeAcceptsValue,
tsAssertTypesExactlyEqual,
tsAssertExtends
} from '@papb/assorted-ts-utils/assert';

type T1 = StrictEqual<{ a: 1 }, { a: 1 }>;
//=> type T1 = true

Expand Down Expand Up @@ -78,6 +86,15 @@ type T7 = IfAny<unknown, 'hello', 'world'>;

type T8 = IfNever<1 & 2, 'hello', 'world'>;
//=> type T8 = 'hello';

type T9 = { a: string; b: string | number };
const someValue =
tsAssertTypeAcceptsValue()<T9>()(
// Autocomplete for `a` and `b` available on the object below
{ a: 'hello', b: 123 } as const
);
tsAssertTypesExactlyEqual()<typeof someValue>()<{ a: 'hello'; b: 123 }>();
tsAssertExtends()<typeof someValue>()<T9>();
```


Expand Down
39 changes: 36 additions & 3 deletions source/assert.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
export function tsAssertIsSupertype<A, B extends A>() {}
export function tsAssertTrue<T extends true>() {}
export function tsAssertFalse<T extends false>() {}
import { IfStrictEqual, IfStrictExtends } from '.';

const noop = (() => {});
const identity = ((value: any) => value);

type Pass = [];
type Fail = [never, never];

function tsCreateExactAsserter<Type>(): (<T extends Type = never>(...mismatch: IfStrictEqual<T, Type, Pass, Fail>) => void) {
return noop;
}

export const tsAssertTypesExactlyEqual = () => tsCreateExactAsserter;
export const tsAssertExtends = () => <A>() => <B>(...mismatch: A extends B ? Pass : Fail) => undefined;
export const tsAssertStrictExtends = () => <A>() => <B>(...mismatch: IfStrictExtends<A, B, Pass, Fail>) => undefined;
export const tsAssertIsAssignable = tsAssertExtends;

export function tsAssertNever<T extends never>(): void;
export function tsAssertNever(never: never): void;
export function tsAssertNever(never?: any): void {}

export const tsAssertTrue = tsCreateExactAsserter<true>();
export const tsAssertFalse = tsCreateExactAsserter<false>();
export const tsAssertAny = tsCreateExactAsserter<any>();
export const tsAssertUnknown = tsCreateExactAsserter<unknown>();

function tsCreateExactInferAsserter<Type>(): (<T extends Type = never>(value: T, ...mismatch: IfStrictEqual<T, Type, Pass, Fail>) => T) {
return identity as any;
}

function tsCreateAssignableInferAsserter<Type>(): (<T extends Type = never>(value: T) => T) {
return identity as any;
}

export const tsAssertTypeExactlyDescribesValue = () => tsCreateExactInferAsserter;
export const tsAssertTypeAcceptsValue = () => tsCreateAssignableInferAsserter;
9 changes: 2 additions & 7 deletions source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export type IfNever<T, A, B> = [T] extends [never] ? A : B;
export type IfAny<T, A, B> = 0 extends (1 & T) ? A : B;
export type IfUnknown<T, A, B> = [unknown] extends [T] ? IfAny<T, B, A> : B;

export type StrictExtends<A, B> = [A] extends [B] ? true : false;
export type IfStrictExtends<A, B, ThenType, ElseType> = [A] extends [B] ? ThenType : ElseType;
export type StrictExtends<A, B> = IfStrictExtends<A, B, true, false>;
export type BidirectionalStrictExtends<A, B> = And<[StrictExtends<A, B>, StrictExtends<B, A>]>;

export type IfNotExtendsCoalesce<T, ShouldExtend, Fallback> =
Expand Down Expand Up @@ -70,12 +71,6 @@ export type StrictEqual<A, B> = IfStrictEqual<A, B, true, false>;

/// -------------------------------------------------------------------------------------

export type IfIdenticalInternalTSRepresentation<A, B, ThenType, ElseType> =
(<T>() => [T] extends [A] ? 1 : 2) extends
(<T>() => [T] extends [B] ? 1 : 2) ? ThenType : ElseType;

/// -------------------------------------------------------------------------------------

export type ReplaceAllTypeOccurrences<T, OldType, NewType> =
IfStrictEqual<T, OldType, NewType,
IfAny<T, T,
Expand Down
45 changes: 45 additions & 0 deletions test/assert/any-unknown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { tsAssertAny, tsAssertUnknown } from '../../source/assert';


tsAssertAny<any>();
tsAssertUnknown<unknown>();


// @ts-expect-error
tsAssertAny();
// @ts-expect-error
tsAssertAny<never>();
// @ts-expect-error
tsAssertAny<void>();
// @ts-expect-error
tsAssertAny<undefined>();
// @ts-expect-error
tsAssertAny<boolean>();
// @ts-expect-error
tsAssertAny<{}>();
// @ts-expect-error
tsAssertAny<unknown>();


// @ts-expect-error
tsAssertAny(0 as any); // eslint-disable-line @typescript-eslint/no-unsafe-argument


// @ts-expect-error
tsAssertUnknown();
// @ts-expect-error
tsAssertUnknown<never>();
// @ts-expect-error
tsAssertUnknown<void>();
// @ts-expect-error
tsAssertUnknown<undefined>();
// @ts-expect-error
tsAssertUnknown<boolean>();
// @ts-expect-error
tsAssertUnknown<{}>();
// @ts-expect-error
tsAssertUnknown<any>();


// @ts-expect-error
tsAssertUnknown(0 as unknown);
29 changes: 29 additions & 0 deletions test/assert/never.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { tsAssertNever } from '../../source/assert';


declare const neverValue: never;
tsAssertNever<never>();
tsAssertNever(neverValue);
tsAssertNever(0 as unknown as never);


// @ts-expect-error
tsAssertNever<void>();
// @ts-expect-error
tsAssertNever<undefined>();
// @ts-expect-error
tsAssertNever<boolean>();
// @ts-expect-error
tsAssertNever<{}>();
// @ts-expect-error
tsAssertNever<unknown>();
// @ts-expect-error
tsAssertNever<any>();


// @ts-expect-error
tsAssertNever(undefined);
// @ts-expect-error
tsAssertNever(null);
// @ts-expect-error
tsAssertNever({});
53 changes: 53 additions & 0 deletions test/assert/true-false.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { tsAssertTrue, tsAssertFalse } from '../../source/assert';


tsAssertTrue<true>();
tsAssertFalse<false>();


// @ts-expect-error
tsAssertTrue();
// @ts-expect-error
tsAssertTrue<never>();
// @ts-expect-error
tsAssertTrue<void>();
// @ts-expect-error
tsAssertTrue<undefined>();
// @ts-expect-error
tsAssertTrue<boolean>();
// @ts-expect-error
tsAssertTrue<{}>();
// @ts-expect-error
tsAssertTrue<unknown>();
// @ts-expect-error
tsAssertTrue<any>();


// @ts-expect-error
tsAssertTrue(true);
// @ts-expect-error
tsAssertTrue(true as const);


// @ts-expect-error
tsAssertFalse();
// @ts-expect-error
tsAssertFalse<never>();
// @ts-expect-error
tsAssertFalse<void>();
// @ts-expect-error
tsAssertFalse<undefined>();
// @ts-expect-error
tsAssertFalse<boolean>();
// @ts-expect-error
tsAssertFalse<{}>();
// @ts-expect-error
tsAssertFalse<unknown>();
// @ts-expect-error
tsAssertFalse<any>();


// @ts-expect-error
tsAssertFalse(false);
// @ts-expect-error
tsAssertFalse(false as const);
33 changes: 33 additions & 0 deletions test/assert/type-comparisons.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { tsAssertTypeAcceptsValue, tsAssertTypesExactlyEqual, tsAssertExtends, tsAssertStrictExtends } from '../../source/assert';

type T1 = { a: string; b: string | number };

const x = tsAssertTypeAcceptsValue()<T1>()(
{ a: 'adfasdf', b: 123 } as const
);

tsAssertTypesExactlyEqual()<typeof x>()<{ a: 'adfasdf'; b: 123 }>();
tsAssertExtends()<typeof x>()<T1>();
tsAssertStrictExtends()<typeof x>()<T1>();

// @ts-expect-error
tsAssertTypesExactlyEqual()<typeof x>()<T1>();
// @ts-expect-error
tsAssertExtends()<T1>()<typeof x>();
// @ts-expect-error
tsAssertStrictExtends()<T1>()<typeof x>();

const y = tsAssertTypeAcceptsValue()<T1>()(
{ a: 'adfasdf', b: 123 }
);

tsAssertTypesExactlyEqual()<typeof y>()<{ a: string; b: number }>();
tsAssertExtends()<typeof y>()<T1>();
tsAssertStrictExtends()<typeof y>()<T1>();

// @ts-expect-error
tsAssertTypesExactlyEqual()<typeof y>()<T1>();
// @ts-expect-error
tsAssertExtends()<T1>()<typeof y>();
// @ts-expect-error
tsAssertStrictExtends()<T1>()<typeof y>();
1 change: 0 additions & 1 deletion test/strict-equal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ tsAssertTrue<StrictEqual<unknown, unknown>>();
tsAssertTrue<StrictEqual<never, never>>();
tsAssertTrue<StrictEqual<() => number, (this: unknown) => number>>();
tsAssertTrue<StrictEqual<{ a: 1; b: 2 }, { a: 1 } & { b: 2 }>>();
(<T>() => tsAssertTrue<StrictEqual<T, T>>())();

tsAssertFalse<StrictEqual<number, any>>();
tsAssertFalse<StrictEqual<unknown, any>>();
Expand Down

0 comments on commit f196dc6

Please sign in to comment.