Skip to content

Commit 81836d6

Browse files
feat: zod-lite
1 parent a981101 commit 81836d6

File tree

8 files changed

+185
-0
lines changed

8 files changed

+185
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Zod Lite
2+
3+
> A [Learning TypeScript > Type Operations](https://learning-typescript.com/type-operations) 🍲 entree project.
4+
5+
Kneel before Zod!
6+
7+
## Setup
8+
9+
In one terminal, run the TypeScript compiler via the `tsc` script.
10+
For example, to start the TypeScript compiler in watch mode:
11+
12+
```shell
13+
npm run tsc -- --watch
14+
```
15+
16+
In another terminal, run Jest via the `test` script.
17+
For example, to start tests in watch mode:
18+
19+
```shell
20+
npm run test -- --watch
21+
```
22+
23+
## Specification
24+
25+
Create and export the following functions from `src/index.ts`:
26+
27+
- `string()`: creates a schema object representing any primitive string
28+
- `literal<Value>(value)`: creates a schema object of a literal string value
29+
- `union<Values>(values)`: creates a schema object representing multiple allowed type constituents
30+
- `object<Properties>(properties)`: creates a schema object representing an object
31+
32+
Additionally, export an `type Infer<Schema>` that creates a TypeScript type for a schema.
33+
34+
## Example
35+
36+
This code:
37+
38+
```ts
39+
import * as z from "./index";
40+
41+
const spySchema = z.object({
42+
disguise: z.string(),
43+
moniker: z.literal("007"),
44+
plan: z.union([z.literal("active"), z.literal("improvising")]),
45+
});
46+
47+
type Spy = z.Infer<typeof spySchema>;
48+
```
49+
50+
...create a `Spy` type equivalent to:
51+
52+
```ts
53+
type Spy = {
54+
disguise: string;
55+
moniker: "007";
56+
plan: "active" | "improvising";
57+
};
58+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"label": "🍲 Zod Lite",
3+
"position": 3
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
transform: {
3+
"^.+\\.(t|j)sx?$": [
4+
"@swc/jest",
5+
{
6+
jsc: {
7+
target: "es2021",
8+
},
9+
},
10+
],
11+
},
12+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "the-narrow-trail",
3+
"scripts": {
4+
"test": "jest",
5+
"test:solutions": "cross-env TEST_SOLUTIONS=1 jest",
6+
"tsc": "tsc"
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { describe, jest, test } from "@jest/globals";
2+
import { expectType } from "tsd";
3+
4+
import * as index from "./index";
5+
import * as solution from "./solution";
6+
7+
const z = process.env.TEST_SOLUTIONS ? solution : index;
8+
9+
const mockRandom = jest.spyOn(Math, "random");
10+
11+
describe("zod-lite", () => {
12+
test("Spy", () => {
13+
const spySchema = z.object({
14+
disguise: z.string(),
15+
moniker: z.literal("007"),
16+
plan: z.union([z.literal("active"), z.literal("improvising")]),
17+
});
18+
expectType<{
19+
disguise: string;
20+
moniker: "007";
21+
plan: "active" | "improvising";
22+
}>({} as index.Infer<typeof spySchema>);
23+
});
24+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Write your functions and type here! ✨
2+
// You'll need to export them so the tests can use them.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Primitives
2+
3+
export interface SchemaString {
4+
primitive: "string";
5+
zType: "primitive";
6+
}
7+
8+
export const string = (): SchemaString => ({
9+
primitive: "string",
10+
zType: "primitive",
11+
});
12+
13+
// Literals
14+
export interface SchemaLiteral<Value> {
15+
value: Value;
16+
zType: "literal";
17+
}
18+
19+
export const literal = <Value extends string>(
20+
value: Value
21+
): SchemaLiteral<Value> => ({
22+
value,
23+
zType: "literal",
24+
});
25+
26+
// Unions
27+
export interface SchemaUnion<Values extends any[]> {
28+
values: Values;
29+
zType: "union";
30+
}
31+
32+
export const union = <Values extends any[]>(
33+
values: Values
34+
): SchemaUnion<Values> => ({
35+
values,
36+
zType: "union",
37+
});
38+
39+
export type UnwrapSchemaUnion<Values> = Values extends (infer Value)[]
40+
? Infer<Value>
41+
: never;
42+
43+
// Objects
44+
export interface SchemaObject<Properties> {
45+
properties: Properties;
46+
zType: "object";
47+
}
48+
49+
export const object = <Properties>(
50+
properties: Properties
51+
): SchemaObject<Properties> => ({
52+
properties,
53+
zType: "object",
54+
});
55+
56+
export type UnwrapSchemaObject<Properties> = {
57+
[K in keyof Properties]: Infer<Properties[K]>;
58+
};
59+
60+
export type Infer<Schema> =
61+
// Primitives (string)
62+
Schema extends SchemaString
63+
? string
64+
: // Literals
65+
Schema extends SchemaLiteral<infer Value>
66+
? Value
67+
: // Unions
68+
Schema extends SchemaUnion<infer Values>
69+
? UnwrapSchemaUnion<Values>
70+
: // Objects
71+
Schema extends SchemaObject<infer Properties>
72+
? UnwrapSchemaObject<Properties>
73+
: never;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"include": ["src"]
4+
}

0 commit comments

Comments
 (0)