Skip to content

Commit

Permalink
feat: invert inference in generics
Browse files Browse the repository at this point in the history
  • Loading branch information
yamiteru committed Apr 29, 2024
1 parent 5a556e5 commit 288d725
Show file tree
Hide file tree
Showing 14 changed files with 57 additions and 131 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@
"access": "public"
},
"dependencies": {
"@the-minimal/types": "0.3.6"
"@the-minimal/types": "0.4.0"
}
}
2 changes: 1 addition & 1 deletion src/assertions/and/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export type Intersection<$Values extends unknown[]> = $Values extends [
infer $Head,
...infer $Tail,
]
? $Tail extends [infer _1, ...infer _2]
? $Tail extends [infer _1]
? $Head & Intersection<$Tail>
: $Head
: never;
14 changes: 5 additions & 9 deletions src/assertions/and2/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that both assertions pass.
Expand All @@ -20,10 +16,10 @@ import type {
* ```
*/
export const and2 =
<$Assertion1 extends UnknownAssertion, $Assertion2 extends UnknownAssertion>(
assertion1: $Assertion1,
assertion2: $Assertion2,
): Assertion<InferAssertion<$Assertion1> & InferAssertion<$Assertion2>> =>
<$Value1, $Value2>(
assertion1: Assertion<$Value1>,
assertion2: Assertion<$Value2>,
): Assertion<$Value1 & $Value2> =>
(v: unknown) => {
assertion1(v);
assertion2(v);
Expand Down
24 changes: 6 additions & 18 deletions src/assertions/and3/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that all three assertions pass.
Expand All @@ -22,19 +18,11 @@ import type {
* ```
*/
export const and3 =
<
$Assertion1 extends UnknownAssertion,
$Assertion2 extends UnknownAssertion,
$Assertion3 extends UnknownAssertion,
>(
assertion1: $Assertion1,
assertion2: $Assertion2,
assertion3: $Assertion3,
): Assertion<
InferAssertion<$Assertion1> &
InferAssertion<$Assertion2> &
InferAssertion<$Assertion3>
> =>
<$Value1, $Value2, $Value3>(
assertion1: Assertion<$Value1>,
assertion2: Assertion<$Value2>,
assertion3: Assertion<$Value3>,
): Assertion<$Value1 & $Value2 & $Value3> =>
(v: unknown) => {
assertion1(v);
assertion2(v);
Expand Down
4 changes: 2 additions & 2 deletions src/assertions/array/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isArray } from "@assertions/isArray";
import type { UnknownAssertion } from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that assertion passes for each element of the array.
Expand All @@ -15,7 +15,7 @@ import type { UnknownAssertion } from "@the-minimal/types";
* ```
*/
export const array =
<$Assertion extends UnknownAssertion>(assertion: $Assertion) =>
<$Value>(assertion: Assertion<$Value>): Assertion<$Value[]> =>
(v: unknown) => {
isArray(v);

Expand Down
11 changes: 2 additions & 9 deletions src/assertions/nullable/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type {
Assertion,
InferAssertion,
Nullable,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion, Nullable } from "@the-minimal/types";

/**
* Checks if the assertion passes or if the value is null.
Expand All @@ -20,8 +15,6 @@ import type {
* ```
*/
export const nullable =
<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
): Assertion<Nullable<InferAssertion<$Assertion>>> =>
<$Value>(assertion: Assertion<$Value>): Assertion<Nullable<$Value>> =>
(v: unknown) =>
v === null || (assertion as any)(v);
11 changes: 2 additions & 9 deletions src/assertions/nullish/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type {
Assertion,
InferAssertion,
Nullish,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion, Nullish } from "@the-minimal/types";

/**
* Checks if the assertion passes or if the value is null or undefined.
Expand All @@ -21,8 +16,6 @@ import type {
* ```
*/
export const nullish =
<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
): Assertion<Nullish<InferAssertion<$Assertion>>> =>
<$Value>(assertion: Assertion<$Value>): Assertion<Nullish<$Value>> =>
(v: unknown) =>
v === null || v === undefined || (assertion as any)(v);
16 changes: 6 additions & 10 deletions src/assertions/object/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { isObject } from "@assertions/isObject";
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that assertion passes for each key/value of the object.
Expand All @@ -29,11 +25,11 @@ import type {
* }); // passes
* ```
*/
export const object = <$Schema extends Record<string, UnknownAssertion>>(
schema: $Schema,
): Assertion<{
[$Key in keyof $Schema]: InferAssertion<$Schema[$Key]>;
}> => {
export const object = <$Schema extends Record<string, unknown>>(
schema: {
[$Key in keyof $Schema]: Assertion<$Schema[$Key]>;
},
): Assertion<$Schema> => {
const keys = Object.keys(schema);

return (v: unknown) => {
Expand Down
11 changes: 2 additions & 9 deletions src/assertions/optional/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type {
Assertion,
InferAssertion,
Optional,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion, Optional } from "@the-minimal/types";

/**
* Checks if the assertion passes or if the value is undefined.
Expand All @@ -20,8 +15,6 @@ import type {
* ```
*/
export const optional =
<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
): Assertion<Optional<InferAssertion<$Assertion>>> =>
<$Value>(assertion: Assertion<$Value>): Assertion<Optional<$Value>> =>
(v: unknown) =>
v === undefined || (assertion as any)(v);
18 changes: 6 additions & 12 deletions src/assertions/or/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { error } from "@error";
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks if one of the assertions passes.
Expand All @@ -26,13 +22,11 @@ import type {
* ```
*/
export const or =
<const $Assertions extends UnknownAssertion[]>(
assertions: $Assertions,
): Assertion<
{
[$Key in keyof $Assertions]: InferAssertion<$Assertions[$Key]>;
}[number]
> =>
<const $Values extends unknown[]>(
assertions: {
[$Key in keyof $Values]: Assertion<$Values[$Key]>;
},
): Assertion<$Values[number]> =>
(v: unknown) => {
for (let i = 0; i < assertions.length; ++i) {
try {
Expand Down
14 changes: 5 additions & 9 deletions src/assertions/or2/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks if one of two assertions passes.
Expand All @@ -23,10 +19,10 @@ import type {
* ```
*/
export const or2 =
<$Assertion1 extends UnknownAssertion, $Assertion2 extends UnknownAssertion>(
assertion1: $Assertion1,
assertion2: $Assertion2,
): Assertion<InferAssertion<$Assertion1> | InferAssertion<$Assertion2>> =>
<$Value1, $Value2>(
assertion1: Assertion<$Value1>,
assertion2: Assertion<$Value2>,
): Assertion<$Value1 | $Value2> =>
(v: unknown) => {
try {
assertion1(v);
Expand Down
24 changes: 6 additions & 18 deletions src/assertions/or3/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks if one of two assertions passes.
Expand All @@ -23,19 +19,11 @@ import type {
* ```
*/
export const or3 =
<
$Assertion1 extends UnknownAssertion,
$Assertion2 extends UnknownAssertion,
$Assertion3 extends UnknownAssertion,
>(
assertion1: $Assertion1,
assertion2: $Assertion2,
assertion3: $Assertion3,
): Assertion<
| InferAssertion<$Assertion1>
| InferAssertion<$Assertion2>
| InferAssertion<$Assertion3>
> =>
<$Value1, $Value2, $Value3>(
assertion1: Assertion<$Value1>,
assertion2: Assertion<$Value2>,
assertion3: Assertion<$Value3>,
): Assertion<$Value1 | $Value2 | $Value3> =>
(v: unknown) => {
try {
assertion1(v);
Expand Down
16 changes: 6 additions & 10 deletions src/assertions/tuple/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { isArray } from "@assertions/isArray";
import type {
Assertion,
InferAssertion,
UnknownAssertion,
} from "@the-minimal/types";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that assertion passes for each element of the tuple.
Expand All @@ -20,11 +16,11 @@ import type {
* ```
*/
export const tuple =
<const $Assertions extends UnknownAssertion[]>(
assertions: $Assertions,
): Assertion<{
[$Key in keyof $Assertions]: InferAssertion<$Assertions[$Key]>;
}> =>
<const $Values extends unknown[]>(
assertions: {
[$Key in keyof $Values]: Assertion<$Values[$Key]>;
},
): Assertion<$Values> =>
(v: unknown) => {
isArray(v);

Expand Down
21 changes: 7 additions & 14 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import type { UnknownAssertion } from "@the-minimal/types";
import type { Infer } from "@types";
import type { Assertion } from "@the-minimal/types";

export function assert<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
export function assert<$Value>(
assertion: Assertion<$Value>,
value: unknown,
): asserts value is Infer<$Assertion> {
): asserts value is $Value {
(assertion as any)(value);
}

export function parse<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
value: unknown,
) {
export function parse<$Value>(assertion: Assertion<$Value>, value: unknown) {
(assertion as any)(value);

return value as Infer<$Assertion>;
return value as $Value;
}

export function is<$Assertion extends UnknownAssertion>(
assertion: $Assertion,
value: unknown,
): value is Infer<$Assertion> {
export function is<$Value>(assertion: $Value, value: unknown): value is $Value {
try {
(assertion as any)(value);

Expand Down

0 comments on commit 288d725

Please sign in to comment.