Skip to content

Commit 18cce08

Browse files
committed
🐛 Fix asOptional and asReadonly for type
1 parent 1ab032a commit 18cce08

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

as/optional.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { rewriteName } from "../_funcutil.ts";
2-
import type { Predicate } from "../type.ts";
2+
import type { Predicate, PredicateType } from "../type.ts";
33
import {
44
annotate,
55
hasAnnotation,
@@ -25,21 +25,30 @@ import {
2525
* }
2626
* ```
2727
*/
28-
export function asOptional<T>(
29-
pred: Predicate<T>,
30-
): Predicate<T | undefined> & WithOptional<T> {
28+
export function asOptional<P extends Predicate<unknown>>(
29+
pred: P,
30+
):
31+
& Extract<P, Predicate<PredicateType<P>>>
32+
& Predicate<PredicateType<P> | undefined>
33+
& WithOptional<PredicateType<P>> {
3134
if (hasAnnotation(pred, "optional")) {
32-
return pred as Predicate<T | undefined> & WithOptional<T>;
35+
return pred as
36+
& Extract<P, Predicate<PredicateType<P>>>
37+
& Predicate<PredicateType<P> | undefined>
38+
& WithOptional<PredicateType<P>>;
3339
}
3440
return rewriteName(
3541
annotate(
36-
(x: unknown): x is T | undefined => x === undefined || pred(x),
42+
(x: unknown) => x === undefined || pred(x),
3743
"optional",
3844
pred,
3945
),
4046
"asOptional",
4147
pred,
42-
) as Predicate<T | undefined> & WithOptional<T>;
48+
) as unknown as
49+
& Extract<P, Predicate<PredicateType<P>>>
50+
& Predicate<PredicateType<P> | undefined>
51+
& WithOptional<PredicateType<P>>;
4352
}
4453

4554
/**

as/readonly.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { rewriteName } from "../_funcutil.ts";
2-
import type { Predicate } from "../type.ts";
2+
import type { Predicate, PredicateType } from "../type.ts";
33
import {
44
annotate,
55
hasAnnotation,
@@ -25,21 +25,21 @@ import {
2525
* }
2626
* ```
2727
*/
28-
export function asReadonly<T>(
29-
pred: Predicate<T>,
30-
): Predicate<T> & WithReadonly {
28+
export function asReadonly<P extends Predicate<unknown>>(
29+
pred: P,
30+
): P & WithReadonly {
3131
if (hasAnnotation(pred, "readonly")) {
32-
return pred as Predicate<T> & WithReadonly;
32+
return pred as P & WithReadonly;
3333
}
3434
return rewriteName(
3535
annotate(
36-
(x: unknown): x is T => pred(x),
36+
(x: unknown) => pred(x),
3737
"readonly",
3838
pred,
3939
),
4040
"asReadonly",
4141
pred,
42-
) as Predicate<T> & WithReadonly;
42+
) as unknown as P & WithReadonly;
4343
}
4444

4545
/**

is/object_of_test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { assertSnapshot } from "@std/testing/snapshot";
33
import { assertType } from "@std/testing/types";
44
import { type Equal, testWithExamples } from "../_testutil.ts";
55
import { is } from "./mod.ts";
6+
import { as } from "../as/mod.ts";
67
import { isObjectOf } from "./object_of.ts";
78

89
Deno.test("isObjectOf<T>", async (t) => {
@@ -82,6 +83,32 @@ Deno.test("isObjectOf<T>", async (t) => {
8283
};
8384
assertEquals(isObjectOf(predObj)(date), true, "Value is not an object");
8485
});
86+
await t.step("with asOptional/asReadonly", () => {
87+
const predObj = {
88+
a: as.Readonly(as.Optional(is.String)),
89+
b: as.Optional(as.Readonly(is.String)),
90+
c: as.Readonly(is.String),
91+
d: as.Optional(is.String),
92+
e: as.Unreadonly(as.Unoptional(as.Readonly(as.Optional(is.String)))),
93+
f: as.Unoptional(as.Unreadonly(as.Optional(as.Readonly(is.String)))),
94+
};
95+
const a: unknown = undefined;
96+
if (isObjectOf(predObj)(a)) {
97+
assertType<
98+
Equal<
99+
typeof a,
100+
{
101+
readonly a?: string;
102+
readonly b?: string;
103+
readonly c: string;
104+
d?: string;
105+
e: string;
106+
f: string;
107+
}
108+
>
109+
>(true);
110+
}
111+
});
85112
await testWithExamples(
86113
t,
87114
isObjectOf({ a: (_: unknown): _ is unknown => false }),

0 commit comments

Comments
 (0)