Skip to content

Commit 169e72d

Browse files
committed
Initial commit
0 parents  commit 169e72d

20 files changed

+1407
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.vscode/settings.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"window.zoomLevel": 3,
3+
"editor.formatOnSave": true,
4+
"editor.tabSize": 2,
5+
"explorer.autoReveal": false,
6+
"workbench.statusBar.visible": false,
7+
"workbench.activityBar.visible": false,
8+
"workbench.editor.showTabs": false,
9+
"breadcrumbs.enabled": true,
10+
"editor.minimap.enabled": false,
11+
"workbench.sideBar.location": "right",
12+
"git.enableSmartCommit": true,
13+
"editor.lineNumbers": "off",
14+
"editor.folding": false,
15+
"editor.defaultFormatter": "esbenp.prettier-vscode",
16+
"editor.inlineSuggest.enabled": true,
17+
"xstate.targetEditorBaseUrl": "http://localhost:3000",
18+
}

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Zod Crash Course
2+
3+
This Zod crash course will give you everything you ever needed to know about [Zod](https://github.com/colinhacks/zod) - an amazing library for building type-safe AND runtime-safe applications.
4+
5+
## Quickstart
6+
7+
```sh
8+
# Installs all dependencies
9+
yarn install
10+
11+
# Starts the first exercise
12+
yarn exercise 01
13+
```
14+
15+
## How to take the course
16+
17+
First, start up the [video on YouTube](TODO).
18+
19+
You'll notice that the course is split into exercises. Each exercise is split into a `*.problem.ts` and a `*.solution.ts`.
20+
21+
To take an exercise:
22+
23+
1. Go into `*.problem.ts`
24+
2. Run `yarn exercise 01`, where `01` is the number of the exercise you're on.
25+
26+
The `exercise` script will run TypeScript typechecks and a test suite on the exercise.
27+
28+
This course encourages **active, exploratory learning**. In the video, I'll explain a problem, and **you'll be asked to try to find a solution**. To attempt a solution, you'll need to:
29+
30+
1. Check out [Zod's docs](https://github.com/colinhacks/zod)
31+
2. Try to find something that looks relevant.
32+
3. Give it a go to see if it solves the problem.
33+
34+
You'll know if you've succeeded because the tests will pass.
35+
36+
**If you succeeed**, or **if you get stuck**, unpause the video and check out the `*.solution.ts`. You can see if your solution is better or worse than mine!
37+
38+
## Acknowledgements
39+
40+
Say thanks to Matt on [Twitter](https://twitter.com/mattpocockuk) or by joining his [Discord](https://discord.gg/8S5ujhfTB3). Consider signing up to his [Total TypeScript course](https://totaltypescript.com).

package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "beginners",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"author": "Matt Pocock <[email protected]>",
6+
"license": "MIT",
7+
"devDependencies": {
8+
"@types/node": "^18.6.5",
9+
"chokidar": "^3.5.3",
10+
"cross-fetch": "^3.1.5",
11+
"typescript": "^4.7.4",
12+
"vitest": "^0.21.1"
13+
},
14+
"scripts": {
15+
"exercise": "node scripts/exercise.js",
16+
"solution": "SOLUTION=true node scripts/exercise.js"
17+
},
18+
"dependencies": {
19+
"@types/express": "^4.17.13",
20+
"express": "^4.18.1",
21+
"zod": "^3.17.10"
22+
}
23+
}

scripts/exercise.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { execSync } = require("child_process");
2+
const fs = require("fs");
3+
const path = require("path");
4+
const chokidar = require("chokidar");
5+
6+
const srcPath = path.resolve(__dirname, "../src");
7+
const tsconfigPath = path.resolve(__dirname, "../tsconfig.json");
8+
9+
const [, , exercise] = process.argv;
10+
11+
if (!exercise) {
12+
console.log("Please specify an exercise");
13+
process.exit(1);
14+
}
15+
16+
const allExercises = fs.readdirSync(srcPath);
17+
18+
let suffix = ".problem.ts";
19+
20+
if (process.env.SOLUTION) {
21+
suffix = ".solution.ts";
22+
}
23+
24+
const exercisePath = allExercises.find(
25+
(exercisePath) =>
26+
exercisePath.startsWith(exercise) && exercisePath.endsWith(suffix),
27+
);
28+
29+
if (!exercisePath) {
30+
console.log(`Exercise ${exercise} not found`);
31+
process.exit(1);
32+
}
33+
34+
const exerciseFile = path.resolve(srcPath, exercisePath);
35+
36+
// One-liner for current directory
37+
chokidar.watch(exerciseFile).on("all", (event, path) => {
38+
try {
39+
console.clear();
40+
console.log("Running tests...");
41+
execSync(`vitest run ${exerciseFile} --passWithNoTests`, {
42+
stdio: "inherit",
43+
});
44+
console.log("Checking types...");
45+
execSync(`tsc ${exerciseFile} --noEmit --strict`, {
46+
stdio: "inherit",
47+
});
48+
console.log("Typecheck complete. You finished the exercise!");
49+
} catch (e) {
50+
console.log("Failed. Try again!");
51+
}
52+
});

scripts/setup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { fetch } from "cross-fetch";
2+
3+
global.fetch = fetch;

src/01-number.problem.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { expect, it } from "vitest";
2+
3+
export const addTwoNumbers = (a, b) => {
4+
return a + b;
5+
};
6+
7+
it("Should add the two numbers together", () => {
8+
expect(addTwoNumbers(2, 4)).toEqual(6);
9+
expect(addTwoNumbers(10, 10)).toEqual(20);
10+
});

src/01-number.solution.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { expect, it } from "vitest";
2+
3+
export const addTwoNumbers = (a: number, b: number) => {
4+
return a + b;
5+
};
6+
7+
it("Should add the two numbers together", () => {
8+
expect(addTwoNumbers(2, 4)).toEqual(6);
9+
expect(addTwoNumbers(10, 10)).toEqual(20);
10+
});

src/02-object-param.problem.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect, it } from "vitest";
2+
3+
export const addTwoNumbers = (params) => {
4+
return params.first + params.second;
5+
};
6+
7+
it("Should add the two numbers together", () => {
8+
expect(
9+
addTwoNumbers({
10+
first: 2,
11+
second: 4,
12+
}),
13+
).toEqual(6);
14+
15+
expect(
16+
addTwoNumbers({
17+
first: 10,
18+
second: 20,
19+
}),
20+
).toEqual(20);
21+
});

src/02-object-param.solution.1.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect, it } from "vitest";
2+
3+
export const addTwoNumbers = (params: { first: number; second: number }) => {
4+
return params.first + params.second;
5+
};
6+
7+
it("Should add the two numbers together", () => {
8+
expect(
9+
addTwoNumbers({
10+
first: 2,
11+
second: 4,
12+
}),
13+
).toEqual(6);
14+
15+
expect(
16+
addTwoNumbers({
17+
first: 10,
18+
second: 20,
19+
}),
20+
).toEqual(20);
21+
});

src/02-object-param.solution.2.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { expect, it } from "vitest";
2+
3+
type AddTwoNumbersArgs = {
4+
first: number;
5+
second: number;
6+
};
7+
8+
export const addTwoNumbers = (params: AddTwoNumbersArgs) => {
9+
return params.first + params.second;
10+
};
11+
12+
it("Should add the two numbers together", () => {
13+
expect(
14+
addTwoNumbers({
15+
first: 2,
16+
second: 4,
17+
}),
18+
).toEqual(6);
19+
20+
expect(
21+
addTwoNumbers({
22+
first: 10,
23+
second: 20,
24+
}),
25+
).toEqual(20);
26+
});

src/02-object-param.solution.3.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { expect, it } from "vitest";
2+
3+
interface AddTwoNumbersArgs {
4+
first: number;
5+
second: number;
6+
}
7+
8+
export const addTwoNumbers = (params: AddTwoNumbersArgs) => {
9+
return params.first + params.second;
10+
};
11+
12+
it("Should add the two numbers together", () => {
13+
expect(
14+
addTwoNumbers({
15+
first: 2,
16+
second: 4,
17+
}),
18+
).toEqual(6);
19+
20+
expect(
21+
addTwoNumbers({
22+
first: 10,
23+
second: 20,
24+
}),
25+
).toEqual(20);
26+
});

src/03-optional-properties.problem.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect, it } from "vitest";
2+
3+
export const getName = (params: { first: string; last: string }) => {
4+
if (params.last) {
5+
return `${params.first} ${params.last}`;
6+
}
7+
return params.first;
8+
};
9+
10+
it("Should work with just the first name", () => {
11+
const name = getName({
12+
first: "Matt",
13+
});
14+
15+
expect(name).toEqual("Matt");
16+
});
17+
18+
it("Should work with the first and last name", () => {
19+
const name = getName({
20+
first: "Matt",
21+
last: "Pocock",
22+
});
23+
24+
expect(name).toEqual("Matt Pocock");
25+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect, it } from "vitest";
2+
3+
export const getName = (params: { first: string; last?: string }) => {
4+
if (params.last) {
5+
return `${params.first} ${params.last}`;
6+
}
7+
return params.first;
8+
};
9+
10+
it("Should work with just the first name", () => {
11+
const name = getName({
12+
first: "Matt",
13+
});
14+
15+
expect(name).toEqual("Matt");
16+
});
17+
18+
it("Should work with the first and last name", () => {
19+
const name = getName({
20+
first: "Matt",
21+
last: "Pocock",
22+
});
23+
24+
expect(name).toEqual("Matt Pocock");
25+
});

src/04-optional-params.problem.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect, it } from "vitest";
2+
3+
export const getName = (first: string, last: string) => {
4+
if (last) {
5+
return `${first} ${last}`;
6+
}
7+
return first;
8+
};
9+
10+
it("Should work with just the first name", () => {
11+
const name = getName("Matt");
12+
13+
expect(name).toEqual("Matt");
14+
});
15+
16+
it("Should work with the first and last name", () => {
17+
const name = getName("Matt", "Pocock");
18+
19+
expect(name).toEqual("Matt Pocock");
20+
});

src/04-optional-params.solution.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect, it } from "vitest";
2+
3+
export const getName = (first: string, last?: string) => {
4+
if (last) {
5+
return `${first} ${last}`;
6+
}
7+
return first;
8+
};
9+
10+
it("Should work with just the first name", () => {
11+
const name = getName("Matt");
12+
13+
expect(name).toEqual("Matt");
14+
});
15+
16+
it("Should work with the first and last name", () => {
17+
const name = getName("Matt", "Pocock");
18+
19+
expect(name).toEqual("Matt Pocock");
20+
});

src/helpers/type-utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
export type Expect<T extends true> = T;
2+
export type ExpectTrue<T extends true> = T;
3+
export type ExpectFalse<T extends false> = T;
4+
export type IsTrue<T extends true> = T;
5+
export type IsFalse<T extends false> = T;
6+
7+
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
8+
T,
9+
>() => T extends Y ? 1 : 2
10+
? true
11+
: false;
12+
export type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true;
13+
14+
// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
15+
export type IsAny<T> = 0 extends 1 & T ? true : false;
16+
export type NotAny<T> = true extends IsAny<T> ? false : true;
17+
18+
export type Debug<T> = { [K in keyof T]: T[K] };
19+
export type MergeInsertions<T> = T extends object
20+
? { [K in keyof T]: MergeInsertions<T[K]> }
21+
: T;
22+
23+
export type Alike<X, Y> = Equal<MergeInsertions<X>, MergeInsertions<Y>>;
24+
25+
export type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE
26+
? true
27+
: false;
28+
export type ExpectValidArgs<
29+
FUNC extends (...args: any[]) => any,
30+
ARGS extends any[],
31+
> = ARGS extends Parameters<FUNC> ? true : false;
32+
33+
export type UnionToIntersection<U> = (
34+
U extends any ? (k: U) => void : never
35+
) extends (k: infer I) => void
36+
? I
37+
: never;

0 commit comments

Comments
 (0)