Skip to content

Commit f196dc6

Browse files
committed
Improve assert utils
Also removed broken `IfIdenticalInternalTSRepresentation` type.
1 parent cc1c6bc commit f196dc6

11 files changed

+238
-31
lines changed

.github/workflows/ci.yml

+10-8
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ on: [push, pull_request]
33

44
jobs:
55
test:
6+
strategy:
7+
fail-fast: false
8+
matrix:
9+
ts-version: ['4.1', '4.2', '4.3']
10+
name: TypeScript ${{ matrix.ts-version }}
611
runs-on: ubuntu-latest
712
steps:
8-
-
9-
uses: actions/checkout@v2
10-
-
11-
name: Use Node.js 12.x
13+
- uses: actions/checkout@v2
14+
- name: Use Node.js 12.x
1215
uses: actions/setup-node@v1
1316
with:
1417
node-version: 12.x
15-
-
16-
run: npm install
17-
-
18-
run: npm test
18+
- run: npm install
19+
- run: npm install --save-dev typescript@~${{ matrix.ts-version }}
20+
- run: npm test

.xo-config.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
22
"rules": {
3+
"unicorn/prevent-abbreviations": "off",
4+
"no-multiple-empty-lines": "off",
5+
"linebreak-style": ["error", "unix"],
6+
"object-curly-spacing": ["error", "always"],
7+
"@typescript-eslint/no-unused-vars": "off",
8+
"@typescript-eslint/no-unsafe-return": "off",
39
"@typescript-eslint/no-empty-function": "off",
410
"@typescript-eslint/ban-types": "off",
511
"@typescript-eslint/indent": "off",
6-
"unicorn/prevent-abbreviations": "off",
7-
"linebreak-style": [
8-
"error",
9-
"unix"
10-
],
11-
"object-curly-spacing": [
12-
"error",
13-
"always"
14-
]
12+
"@typescript-eslint/object-curly-spacing": ["error", "always"],
13+
"@typescript-eslint/no-unnecessary-type-arguments": "off",
14+
"@typescript-eslint/no-confusing-void-expression": "off"
1515
}
1616
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
},
3636
"devDependencies": {
3737
"del-cli": "^3.0.1",
38+
"eslint-config-xo-typescript": "^0.41.1",
3839
"np": "^6.5.0",
3940
"typescript": "~4.1.3",
40-
"xo": "^0.36.1"
41+
"xo": "^0.39.1"
4142
},
4243
"publishConfig": {
4344
"access": "public"

readme.md

+19-2
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,22 @@ $ npm install @papb/assorted-ts-utils
2424
## Usage
2525

2626
```ts
27-
import { tsAssertTrue } from '@papb/assorted-ts-utils/assert';
2827
import {
2928
StrictEqual,
3029
ReplaceKeyType,
3130
ParseArray,
32-
ReplaceAllTypeOccurrences
31+
ReplaceAllTypeOccurrences,
32+
IfAny,
33+
IfNever
3334
} from '@papb/assorted-ts-utils';
3435

36+
import {
37+
tsAssertTrue,
38+
tsAssertTypeAcceptsValue,
39+
tsAssertTypesExactlyEqual,
40+
tsAssertExtends
41+
} from '@papb/assorted-ts-utils/assert';
42+
3543
type T1 = StrictEqual<{ a: 1 }, { a: 1 }>;
3644
//=> type T1 = true
3745

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

7987
type T8 = IfNever<1 & 2, 'hello', 'world'>;
8088
//=> type T8 = 'hello';
89+
90+
type T9 = { a: string; b: string | number };
91+
const someValue =
92+
tsAssertTypeAcceptsValue()<T9>()(
93+
// Autocomplete for `a` and `b` available on the object below
94+
{ a: 'hello', b: 123 } as const
95+
);
96+
tsAssertTypesExactlyEqual()<typeof someValue>()<{ a: 'hello'; b: 123 }>();
97+
tsAssertExtends()<typeof someValue>()<T9>();
8198
```
8299

83100

source/assert.ts

+36-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1-
export function tsAssertIsSupertype<A, B extends A>() {}
2-
export function tsAssertTrue<T extends true>() {}
3-
export function tsAssertFalse<T extends false>() {}
1+
import { IfStrictEqual, IfStrictExtends } from '.';
2+
3+
const noop = (() => {});
4+
const identity = ((value: any) => value);
5+
6+
type Pass = [];
7+
type Fail = [never, never];
8+
9+
function tsCreateExactAsserter<Type>(): (<T extends Type = never>(...mismatch: IfStrictEqual<T, Type, Pass, Fail>) => void) {
10+
return noop;
11+
}
12+
13+
export const tsAssertTypesExactlyEqual = () => tsCreateExactAsserter;
14+
export const tsAssertExtends = () => <A>() => <B>(...mismatch: A extends B ? Pass : Fail) => undefined;
15+
export const tsAssertStrictExtends = () => <A>() => <B>(...mismatch: IfStrictExtends<A, B, Pass, Fail>) => undefined;
16+
export const tsAssertIsAssignable = tsAssertExtends;
17+
18+
export function tsAssertNever<T extends never>(): void;
19+
export function tsAssertNever(never: never): void;
20+
export function tsAssertNever(never?: any): void {}
21+
22+
export const tsAssertTrue = tsCreateExactAsserter<true>();
23+
export const tsAssertFalse = tsCreateExactAsserter<false>();
24+
export const tsAssertAny = tsCreateExactAsserter<any>();
25+
export const tsAssertUnknown = tsCreateExactAsserter<unknown>();
26+
27+
function tsCreateExactInferAsserter<Type>(): (<T extends Type = never>(value: T, ...mismatch: IfStrictEqual<T, Type, Pass, Fail>) => T) {
28+
return identity as any;
29+
}
30+
31+
function tsCreateAssignableInferAsserter<Type>(): (<T extends Type = never>(value: T) => T) {
32+
return identity as any;
33+
}
34+
35+
export const tsAssertTypeExactlyDescribesValue = () => tsCreateExactInferAsserter;
36+
export const tsAssertTypeAcceptsValue = () => tsCreateAssignableInferAsserter;

source/index.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export type IfNever<T, A, B> = [T] extends [never] ? A : B;
1414
export type IfAny<T, A, B> = 0 extends (1 & T) ? A : B;
1515
export type IfUnknown<T, A, B> = [unknown] extends [T] ? IfAny<T, B, A> : B;
1616

17-
export type StrictExtends<A, B> = [A] extends [B] ? true : false;
17+
export type IfStrictExtends<A, B, ThenType, ElseType> = [A] extends [B] ? ThenType : ElseType;
18+
export type StrictExtends<A, B> = IfStrictExtends<A, B, true, false>;
1819
export type BidirectionalStrictExtends<A, B> = And<[StrictExtends<A, B>, StrictExtends<B, A>]>;
1920

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

7172
/// -------------------------------------------------------------------------------------
7273

73-
export type IfIdenticalInternalTSRepresentation<A, B, ThenType, ElseType> =
74-
(<T>() => [T] extends [A] ? 1 : 2) extends
75-
(<T>() => [T] extends [B] ? 1 : 2) ? ThenType : ElseType;
76-
77-
/// -------------------------------------------------------------------------------------
78-
7974
export type ReplaceAllTypeOccurrences<T, OldType, NewType> =
8075
IfStrictEqual<T, OldType, NewType,
8176
IfAny<T, T,

test/assert/any-unknown.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { tsAssertAny, tsAssertUnknown } from '../../source/assert';
2+
3+
4+
tsAssertAny<any>();
5+
tsAssertUnknown<unknown>();
6+
7+
8+
// @ts-expect-error
9+
tsAssertAny();
10+
// @ts-expect-error
11+
tsAssertAny<never>();
12+
// @ts-expect-error
13+
tsAssertAny<void>();
14+
// @ts-expect-error
15+
tsAssertAny<undefined>();
16+
// @ts-expect-error
17+
tsAssertAny<boolean>();
18+
// @ts-expect-error
19+
tsAssertAny<{}>();
20+
// @ts-expect-error
21+
tsAssertAny<unknown>();
22+
23+
24+
// @ts-expect-error
25+
tsAssertAny(0 as any); // eslint-disable-line @typescript-eslint/no-unsafe-argument
26+
27+
28+
// @ts-expect-error
29+
tsAssertUnknown();
30+
// @ts-expect-error
31+
tsAssertUnknown<never>();
32+
// @ts-expect-error
33+
tsAssertUnknown<void>();
34+
// @ts-expect-error
35+
tsAssertUnknown<undefined>();
36+
// @ts-expect-error
37+
tsAssertUnknown<boolean>();
38+
// @ts-expect-error
39+
tsAssertUnknown<{}>();
40+
// @ts-expect-error
41+
tsAssertUnknown<any>();
42+
43+
44+
// @ts-expect-error
45+
tsAssertUnknown(0 as unknown);

test/assert/never.test.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { tsAssertNever } from '../../source/assert';
2+
3+
4+
declare const neverValue: never;
5+
tsAssertNever<never>();
6+
tsAssertNever(neverValue);
7+
tsAssertNever(0 as unknown as never);
8+
9+
10+
// @ts-expect-error
11+
tsAssertNever<void>();
12+
// @ts-expect-error
13+
tsAssertNever<undefined>();
14+
// @ts-expect-error
15+
tsAssertNever<boolean>();
16+
// @ts-expect-error
17+
tsAssertNever<{}>();
18+
// @ts-expect-error
19+
tsAssertNever<unknown>();
20+
// @ts-expect-error
21+
tsAssertNever<any>();
22+
23+
24+
// @ts-expect-error
25+
tsAssertNever(undefined);
26+
// @ts-expect-error
27+
tsAssertNever(null);
28+
// @ts-expect-error
29+
tsAssertNever({});

test/assert/true-false.test.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { tsAssertTrue, tsAssertFalse } from '../../source/assert';
2+
3+
4+
tsAssertTrue<true>();
5+
tsAssertFalse<false>();
6+
7+
8+
// @ts-expect-error
9+
tsAssertTrue();
10+
// @ts-expect-error
11+
tsAssertTrue<never>();
12+
// @ts-expect-error
13+
tsAssertTrue<void>();
14+
// @ts-expect-error
15+
tsAssertTrue<undefined>();
16+
// @ts-expect-error
17+
tsAssertTrue<boolean>();
18+
// @ts-expect-error
19+
tsAssertTrue<{}>();
20+
// @ts-expect-error
21+
tsAssertTrue<unknown>();
22+
// @ts-expect-error
23+
tsAssertTrue<any>();
24+
25+
26+
// @ts-expect-error
27+
tsAssertTrue(true);
28+
// @ts-expect-error
29+
tsAssertTrue(true as const);
30+
31+
32+
// @ts-expect-error
33+
tsAssertFalse();
34+
// @ts-expect-error
35+
tsAssertFalse<never>();
36+
// @ts-expect-error
37+
tsAssertFalse<void>();
38+
// @ts-expect-error
39+
tsAssertFalse<undefined>();
40+
// @ts-expect-error
41+
tsAssertFalse<boolean>();
42+
// @ts-expect-error
43+
tsAssertFalse<{}>();
44+
// @ts-expect-error
45+
tsAssertFalse<unknown>();
46+
// @ts-expect-error
47+
tsAssertFalse<any>();
48+
49+
50+
// @ts-expect-error
51+
tsAssertFalse(false);
52+
// @ts-expect-error
53+
tsAssertFalse(false as const);

test/assert/type-comparisons.test.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { tsAssertTypeAcceptsValue, tsAssertTypesExactlyEqual, tsAssertExtends, tsAssertStrictExtends } from '../../source/assert';
2+
3+
type T1 = { a: string; b: string | number };
4+
5+
const x = tsAssertTypeAcceptsValue()<T1>()(
6+
{ a: 'adfasdf', b: 123 } as const
7+
);
8+
9+
tsAssertTypesExactlyEqual()<typeof x>()<{ a: 'adfasdf'; b: 123 }>();
10+
tsAssertExtends()<typeof x>()<T1>();
11+
tsAssertStrictExtends()<typeof x>()<T1>();
12+
13+
// @ts-expect-error
14+
tsAssertTypesExactlyEqual()<typeof x>()<T1>();
15+
// @ts-expect-error
16+
tsAssertExtends()<T1>()<typeof x>();
17+
// @ts-expect-error
18+
tsAssertStrictExtends()<T1>()<typeof x>();
19+
20+
const y = tsAssertTypeAcceptsValue()<T1>()(
21+
{ a: 'adfasdf', b: 123 }
22+
);
23+
24+
tsAssertTypesExactlyEqual()<typeof y>()<{ a: string; b: number }>();
25+
tsAssertExtends()<typeof y>()<T1>();
26+
tsAssertStrictExtends()<typeof y>()<T1>();
27+
28+
// @ts-expect-error
29+
tsAssertTypesExactlyEqual()<typeof y>()<T1>();
30+
// @ts-expect-error
31+
tsAssertExtends()<T1>()<typeof y>();
32+
// @ts-expect-error
33+
tsAssertStrictExtends()<T1>()<typeof y>();

test/strict-equal.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ tsAssertTrue<StrictEqual<unknown, unknown>>();
66
tsAssertTrue<StrictEqual<never, never>>();
77
tsAssertTrue<StrictEqual<() => number, (this: unknown) => number>>();
88
tsAssertTrue<StrictEqual<{ a: 1; b: 2 }, { a: 1 } & { b: 2 }>>();
9-
(<T>() => tsAssertTrue<StrictEqual<T, T>>())();
109

1110
tsAssertFalse<StrictEqual<number, any>>();
1211
tsAssertFalse<StrictEqual<unknown, any>>();

0 commit comments

Comments
 (0)