Repository: total-typescript/beginners-typescript-tutorial
Branch: main
Commit: e430c2da7ab0
Files: 57
Total size: 29.3 KB
Directory structure:
gitextract_69awz6n2/
├── .github/
│ └── workflows/
│ ├── renovate-checks.yml
│ └── section-repos.yml
├── .gitignore
├── .vscode/
│ └── settings.json
├── README.md
├── package.json
├── renovate.json
├── src/
│ ├── 01-number.problem.ts
│ ├── 01-number.solution.ts
│ ├── 02-object-param.problem.ts
│ ├── 02-object-param.solution.1.ts
│ ├── 02-object-param.solution.2.ts
│ ├── 02-object-param.solution.3.ts
│ ├── 03-optional-properties.problem.ts
│ ├── 03-optional-properties.solution.ts
│ ├── 04-optional-params.problem.ts
│ ├── 04-optional-params.solution.ts
│ ├── 05-assigning-types-to-variables.problem.ts
│ ├── 05-assigning-types-to-variables.solution.ts
│ ├── 06-unions.problem.ts
│ ├── 06-unions.solution.ts
│ ├── 07-arrays.problem.ts
│ ├── 07-arrays.solution.1.ts
│ ├── 07-arrays.solution.2.ts
│ ├── 08-function-return-type-annotations.problem.ts
│ ├── 08-function-return-type-annotations.solution.ts
│ ├── 09-promises.problem.ts
│ ├── 09-promises.solution.1.ts
│ ├── 09-promises.solution.2.ts
│ ├── 09-promises.solution.3.ts
│ ├── 10-set.problem.ts
│ ├── 10-set.solution.ts
│ ├── 11-record.problem.ts
│ ├── 11-record.solution.1.ts
│ ├── 11-record.solution.2.ts
│ ├── 11-record.solution.3.ts
│ ├── 12-typeof-narrowing.problem.ts
│ ├── 12-typeof-narrowing.solution.ts
│ ├── 13-catch-blocks.problem.ts
│ ├── 13-catch-blocks.solution.1.ts
│ ├── 13-catch-blocks.solution.2.ts
│ ├── 13-catch-blocks.solution.3.ts
│ ├── 14-extends.problem.ts
│ ├── 14-extends.solution.ts
│ ├── 15-intersection.problem.ts
│ ├── 15-intersection.solution.ts
│ ├── 16-omit-and-pick.problem.ts
│ ├── 16-omit-and-pick.solution.1.ts
│ ├── 16-omit-and-pick.solution.2.ts
│ ├── 17-function-types.problem.ts
│ ├── 17-function-types.solution.1.ts
│ ├── 17-function-types.solution.2.ts
│ ├── 18-function-types-with-promises.problem.ts
│ ├── 18-function-types-with-promises.solution.ts
│ └── helpers/
│ └── type-utils.ts
├── tsconfig.json
└── vite.config.mts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/renovate-checks.yml
================================================
name: Renovate Checks
on:
push:
branches:
- "renovate/**"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Main
uses: actions/checkout@v4
with:
ref: main
path: repo
- name: Install Dependencies in Main
run: (cd repo && npm install)
- name: Create Snapshot In Main
run: (cd repo && npx tt-cli take-snapshot ./snap.md)
- name: Copy Snapshot To Outer Directory
run: mv repo/snap.md ./snap.md
- name: Delete Main Directory
run: rm -rf repo
- name: Checkout Branch
uses: actions/checkout@v4
with:
path: repo
- name: Install Dependencies in Branch
run: (cd repo && npm install)
- name: Move Snapshot To Branch
run: mv ./snap.md repo/snap.md
- name: Compare Snapshot In Branch
run: (cd repo && npx tt-cli compare-snapshot ./snap.md)
================================================
FILE: .github/workflows/section-repos.yml
================================================
name: Create Section Repos
on:
push:
branches:
- "main"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
- run: git config --global user.email "total-typescript@bot.com"
- run: git config --global user.name "Total TypeScript Bot"
- run: npx @total-typescript/exercise-cli@latest create-section-repos
env:
GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
node_modules
tsconfig.temp.json
dist
*.tsbuildinfo
*.prompt.*
.vscode/*.code-snippets
================================================
FILE: .vscode/settings.json
================================================
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"github.copilot.enable": {
"*": false,
},
"explorer.sortOrder": "mixed",
}
================================================
FILE: README.md
================================================
<a href="https://totaltypescript.com/tutorials/beginners-typescript"><img src="https://res.cloudinary.com/total-typescript/image/upload/v1709297838/github--beginngers-typescript_2x_p7vtmw.jpg" alt="beginner typescript tutorial" /></a>
## Quickstart
Take the course on [Total TypeScript](https://totaltypescript.com/tutorials/beginners-typescript). There, you'll find:
- Video explanations for each problem and solution
- Transcripts
- Text explanations
- A built-in Stackblitz editor
```sh
# Installs all dependencies
npm install
# Asks you which exercise you'd like to run, and runs it
npm run exercise
```
## How to take the course
You'll notice that the course is split into exercises. Each exercise is split into a `*.problem` and a `*.solution`.
To take an exercise:
1. Run `npm run exercise`
2. Choose which exercise you'd like to run.
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:
1. Check out [TypeScript's docs](https://www.typescriptlang.org/docs/handbook/intro.html).
1. Try to find something that looks relevant.
1. Give it a go to see if it solves the problem.
You'll know if you've succeeded because the tests will pass.
**If you succeed**, or **if you get stuck**, unpause the video and check out the `*.solution`. You can see if your solution is better or worse than mine!
## Acknowledgements
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).
## Reference
### `npm run exercise`
Alias: `npm run e`
Open a prompt for choosing which exercise you'd like to run.
================================================
FILE: package.json
================================================
{
"name": "beginners-typescript",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"author": "Matt Pocock <mattpocockvoice@gmail.com>",
"license": "GPL-3.0",
"devDependencies": {
"@total-typescript/exercise-cli": "^0.11.0",
"@total-typescript/tsconfig": "^1.0.3",
"@types/node": "^18.6.5",
"cross-fetch": "^3.1.5",
"jsdom": "^25.0.0",
"typescript": "^5.4.5",
"vite-tsconfig-paths": "^5.0.0",
"vitest": "^2.0.0"
},
"scripts": {
"exercise": "tt-cli run",
"e": "npm run exercise",
"solution": "tt-cli run --solution",
"s": "npm run solution",
"prepare": "tt-cli prepare-stackblitz",
"e-01": "tt-cli run 01",
"s-01": "tt-cli run 01 --solution",
"e-02": "tt-cli run 02",
"s-02": "tt-cli run 02 --solution",
"e-03": "tt-cli run 03",
"s-03": "tt-cli run 03 --solution",
"e-04": "tt-cli run 04",
"s-04": "tt-cli run 04 --solution",
"e-05": "tt-cli run 05",
"s-05": "tt-cli run 05 --solution",
"e-06": "tt-cli run 06",
"s-06": "tt-cli run 06 --solution",
"e-07": "tt-cli run 07",
"s-07": "tt-cli run 07 --solution",
"e-08": "tt-cli run 08",
"s-08": "tt-cli run 08 --solution",
"e-09": "tt-cli run 09",
"s-09": "tt-cli run 09 --solution",
"e-10": "tt-cli run 10",
"s-10": "tt-cli run 10 --solution",
"e-11": "tt-cli run 11",
"s-11": "tt-cli run 11 --solution",
"e-12": "tt-cli run 12",
"s-12": "tt-cli run 12 --solution",
"e-13": "tt-cli run 13",
"s-13": "tt-cli run 13 --solution",
"e-14": "tt-cli run 14",
"s-14": "tt-cli run 14 --solution",
"e-15": "tt-cli run 15",
"s-15": "tt-cli run 15 --solution",
"e-16": "tt-cli run 16",
"s-16": "tt-cli run 16 --solution",
"e-17": "tt-cli run 17",
"s-17": "tt-cli run 17 --solution",
"e-18": "tt-cli run 18",
"s-18": "tt-cli run 18 --solution"
},
"dependencies": {
"@types/express": "^4.17.13",
"express": "^4.18.1",
"zod": "^3.17.10"
}
}
================================================
FILE: renovate.json
================================================
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"packageRules": [
{
"packagePatterns": ["*"],
"excludePackagePatterns": [
"typescript",
"vitest",
"jsdom",
"prettier",
"vite-tsconfig-paths",
"react",
"@types/react",
"@total-typescript/exercise-cli",
"zod"
],
"enabled": false
}
]
}
================================================
FILE: src/01-number.problem.ts
================================================
import { expect, it } from "vitest";
export const addTwoNumbers = (a, b) => {
return a + b;
};
it("Should add the two numbers together", () => {
expect(addTwoNumbers(2, 4)).toEqual(6);
expect(addTwoNumbers(10, 10)).toEqual(20);
});
================================================
FILE: src/01-number.solution.ts
================================================
import { expect, it } from "vitest";
export const addTwoNumbers = (a: number, b: number) => {
return a + b;
};
it("Should add the two numbers together", () => {
expect(addTwoNumbers(2, 4)).toEqual(6);
expect(addTwoNumbers(10, 10)).toEqual(20);
});
================================================
FILE: src/02-object-param.problem.ts
================================================
import { expect, it } from "vitest";
export const addTwoNumbers = (params) => {
return params.first + params.second;
};
it("Should add the two numbers together", () => {
expect(
addTwoNumbers({
first: 2,
second: 4,
}),
).toEqual(6);
expect(
addTwoNumbers({
first: 10,
second: 20,
}),
).toEqual(30);
});
================================================
FILE: src/02-object-param.solution.1.ts
================================================
import { expect, it } from "vitest";
export const addTwoNumbers = (params: { first: number; second: number }) => {
return params.first + params.second;
};
it("Should add the two numbers together", () => {
expect(
addTwoNumbers({
first: 2,
second: 4,
}),
).toEqual(6);
expect(
addTwoNumbers({
first: 10,
second: 20,
}),
).toEqual(30);
});
================================================
FILE: src/02-object-param.solution.2.ts
================================================
import { expect, it } from "vitest";
type AddTwoNumbersArgs = {
first: number;
second: number;
};
export const addTwoNumbers = (params: AddTwoNumbersArgs) => {
return params.first + params.second;
};
it("Should add the two numbers together", () => {
expect(
addTwoNumbers({
first: 2,
second: 4,
}),
).toEqual(6);
expect(
addTwoNumbers({
first: 10,
second: 20,
}),
).toEqual(30);
});
================================================
FILE: src/02-object-param.solution.3.ts
================================================
import { expect, it } from "vitest";
interface AddTwoNumbersArgs {
first: number;
second: number;
}
export const addTwoNumbers = (params: AddTwoNumbersArgs) => {
return params.first + params.second;
};
it("Should add the two numbers together", () => {
expect(
addTwoNumbers({
first: 2,
second: 4,
}),
).toEqual(6);
expect(
addTwoNumbers({
first: 10,
second: 20,
}),
).toEqual(30);
});
================================================
FILE: src/03-optional-properties.problem.ts
================================================
import { expect, it } from "vitest";
export const getName = (params: { first: string; last: string }) => {
if (params.last) {
return `${params.first} ${params.last}`;
}
return params.first;
};
it("Should work with just the first name", () => {
const name = getName({
first: "Matt",
});
expect(name).toEqual("Matt");
});
it("Should work with the first and last name", () => {
const name = getName({
first: "Matt",
last: "Pocock",
});
expect(name).toEqual("Matt Pocock");
});
================================================
FILE: src/03-optional-properties.solution.ts
================================================
import { expect, it } from "vitest";
export const getName = (params: { first: string; last?: string }) => {
if (params.last) {
return `${params.first} ${params.last}`;
}
return params.first;
};
it("Should work with just the first name", () => {
const name = getName({
first: "Matt",
});
expect(name).toEqual("Matt");
});
it("Should work with the first and last name", () => {
const name = getName({
first: "Matt",
last: "Pocock",
});
expect(name).toEqual("Matt Pocock");
});
================================================
FILE: src/04-optional-params.problem.ts
================================================
import { expect, it } from "vitest";
export const getName = (first: string, last: string) => {
if (last) {
return `${first} ${last}`;
}
return first;
};
it("Should work with just the first name", () => {
const name = getName("Matt");
expect(name).toEqual("Matt");
});
it("Should work with the first and last name", () => {
const name = getName("Matt", "Pocock");
expect(name).toEqual("Matt Pocock");
});
================================================
FILE: src/04-optional-params.solution.ts
================================================
import { expect, it } from "vitest";
export const getName = (first: string, last?: string) => {
if (last) {
return `${first} ${last}`;
}
return first;
};
it("Should work with just the first name", () => {
const name = getName("Matt");
expect(name).toEqual("Matt");
});
it("Should work with the first and last name", () => {
const name = getName("Matt", "Pocock");
expect(name).toEqual("Matt Pocock");
});
================================================
FILE: src/05-assigning-types-to-variables.problem.ts
================================================
import { expect, it } from "vitest";
interface User {
id: number;
firstName: string;
lastName: string;
isAdmin: boolean;
}
/**
* How do we ensure that defaultUser is of type User
* at THIS LINE - not further down in the code?
*/
const defaultUser = {};
const getUserId = (user: User) => {
return user.id;
};
it("Should get the user id", () => {
expect(getUserId(defaultUser)).toEqual(1);
});
================================================
FILE: src/05-assigning-types-to-variables.solution.ts
================================================
import { expect, it } from "vitest";
interface User {
id: number;
firstName: string;
lastName: string;
isAdmin: boolean;
}
/**
* How do we ensure that defaultUser is of type User
* at THIS LINE - not further down in the code?
*/
const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
isAdmin: true,
};
const getUserId = (user: User) => {
return user.id;
};
it("Should get the user id", () => {
expect(getUserId(defaultUser)).toEqual(1);
});
================================================
FILE: src/06-unions.problem.ts
================================================
interface User {
id: number;
firstName: string;
lastName: string;
/**
* How do we ensure that role is only one of:
* - 'admin'
* - 'user'
* - 'super-admin'
*/
role: string;
}
export const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
// @ts-expect-error
role: "I_SHOULD_NOT_BE_ALLOWED",
};
================================================
FILE: src/06-unions.solution.ts
================================================
interface User {
id: number;
firstName: string;
lastName: string;
/**
* How do we ensure that role is only one of:
* - 'admin'
* - 'user'
* - 'super-admin'
*/
role: "admin" | "user" | "super-admin";
}
export const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
// @ts-expect-error
role: "I_SHOULD_NOT_BE_ALLOWED",
};
================================================
FILE: src/07-arrays.problem.ts
================================================
interface User {
id: number;
firstName: string;
lastName: string;
role: "admin" | "user" | "super-admin";
posts: Post;
}
interface Post {
id: number;
title: string;
}
export const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
role: "admin",
posts: [
{
id: 1,
title: "How I eat so much cheese",
},
{
id: 2,
title: "Why I don't eat more vegetables",
},
],
};
================================================
FILE: src/07-arrays.solution.1.ts
================================================
interface User {
id: number;
firstName: string;
lastName: string;
role: "admin" | "user" | "super-admin";
posts: Post[];
}
interface Post {
id: number;
title: string;
}
export const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
role: "admin",
posts: [
{
id: 1,
title: "How I eat so much cheese",
},
{
id: 2,
title: "Why I don't eat more vegetables",
},
],
};
================================================
FILE: src/07-arrays.solution.2.ts
================================================
interface User {
id: number;
firstName: string;
lastName: string;
role: "admin" | "user" | "super-admin";
posts: Array<Post>;
}
interface Post {
id: number;
title: string;
}
export const defaultUser: User = {
id: 1,
firstName: "Matt",
lastName: "Pocock",
role: "admin",
posts: [
{
id: 1,
title: "How I eat so much cheese",
},
{
id: 2,
title: "Why I don't eat more vegetables",
},
],
};
================================================
FILE: src/08-function-return-type-annotations.problem.ts
================================================
import { expect, it } from "vitest";
interface User {
id: number;
firstName: string;
lastName: string;
role: "admin" | "user" | "super-admin";
posts: Array<Post>;
}
interface Post {
id: number;
title: string;
}
/**
* How do we ensure that makeUser ALWAYS
* returns a user?
*/
const makeUser = () => {
return {};
};
it("Should return a valid user", () => {
const user = makeUser();
expect(user.id).toBeTypeOf("number");
expect(user.firstName).toBeTypeOf("string");
expect(user.lastName).toBeTypeOf("string");
expect(user.role).to.be.oneOf(["super-admin", "admin", "user"]);
expect(user.posts[0].id).toBeTypeOf("number");
expect(user.posts[0].title).toBeTypeOf("string");
});
================================================
FILE: src/08-function-return-type-annotations.solution.ts
================================================
import { expect, it } from "vitest";
interface User {
id: number;
firstName: string;
lastName: string;
role: "admin" | "user" | "super-admin";
posts: Array<Post>;
}
interface Post {
id: number;
title: string;
}
/**
* How do we ensure that makeUser ALWAYS
* returns a user?
*/
const makeUser = (): User => {
return {
id: 1,
firstName: "Matt",
lastName: "Pocock",
role: "admin",
posts: [
{
id: 1,
title: "How I eat so much cheese",
},
],
};
};
it("Should return a valid user", () => {
const user = makeUser();
expect(user.id).toBeTypeOf("number");
expect(user.firstName).toBeTypeOf("string");
expect(user.lastName).toBeTypeOf("string");
expect(user.role).to.be.oneOf(["super-admin", "admin", "user"]);
expect(user.posts[0].id).toBeTypeOf("number");
expect(user.posts[0].title).toBeTypeOf("string");
});
================================================
FILE: src/09-promises.problem.ts
================================================
interface LukeSkywalker {
name: string;
height: string;
mass: string;
hair_color: string;
skin_color: string;
eye_color: string;
birth_year: string;
gender: string;
}
export const fetchLukeSkywalker = async (): LukeSkywalker => {
const data = await fetch("https://swapi.py4e.com/api/people/1").then(
(res) => {
return res.json();
}
);
return data;
};
================================================
FILE: src/09-promises.solution.1.ts
================================================
interface LukeSkywalker {
name: string;
height: string;
mass: string;
hair_color: string;
skin_color: string;
eye_color: string;
birth_year: string;
gender: string;
}
export const fetchLukeSkywalker = async (): Promise<LukeSkywalker> => {
const data = await fetch("https://swapi.py4e.com/api/people/1").then(
(res) => {
return res.json();
}
);
return data;
};
================================================
FILE: src/09-promises.solution.2.ts
================================================
interface LukeSkywalker {
name: string;
height: string;
mass: string;
hair_color: string;
skin_color: string;
eye_color: string;
birth_year: string;
gender: string;
}
export const fetchLukeSkywalker = async () => {
const data = await fetch("https://swapi.py4e.com/api/people/1").then(
(res) => {
return res.json();
}
);
return data as LukeSkywalker;
};
================================================
FILE: src/09-promises.solution.3.ts
================================================
interface LukeSkywalker {
name: string;
height: string;
mass: string;
hair_color: string;
skin_color: string;
eye_color: string;
birth_year: string;
gender: string;
}
export const fetchLukeSkywalker = async () => {
const data: LukeSkywalker = await fetch(
"https://swapi.py4e.com/api/people/1"
).then((res) => {
return res.json();
});
return data;
};
================================================
FILE: src/10-set.problem.ts
================================================
import { expect, it } from "vitest";
import { Equal, Expect } from "./helpers/type-utils";
const guitarists = new Set();
guitarists.add("Jimi Hendrix");
guitarists.add("Eric Clapton");
it("Should contain Jimi and Eric", () => {
expect(guitarists.has("Jimi Hendrix")).toEqual(true);
expect(guitarists.has("Eric Clapton")).toEqual(true);
});
it("Should give a type error when you try to pass a non-string", () => {
// @ts-expect-error
guitarists.add(2);
});
it("Should be typed as an array of strings", () => {
const guitaristsAsArray = Array.from(guitarists);
type tests = [Expect<Equal<typeof guitaristsAsArray, string[]>>];
});
================================================
FILE: src/10-set.solution.ts
================================================
import { expect, it } from "vitest";
import { Equal, Expect } from "./helpers/type-utils";
const guitarists = new Set<string>();
guitarists.add("Jimi Hendrix");
guitarists.add("Eric Clapton");
it("Should contain Jimi and Eric", () => {
expect(guitarists.has("Jimi Hendrix")).toEqual(true);
expect(guitarists.has("Eric Clapton")).toEqual(true);
});
it("Should give a type error when you try to pass a non-string", () => {
// @ts-expect-error
guitarists.add(2);
});
it("Should be typed as an array of strings", () => {
const guitaristsAsArray = Array.from(guitarists);
type tests = [Expect<Equal<typeof guitaristsAsArray, string[]>>];
});
================================================
FILE: src/11-record.problem.ts
================================================
import { expect, it } from "vitest";
const createCache = () => {
const cache = {};
const add = (id: string, value: string) => {
cache[id] = value;
};
const remove = (id: string) => {
delete cache[id];
};
return {
cache,
add,
remove,
};
};
it("Should add values to the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
expect(cache.cache["123"]).toEqual("Matt");
});
it("Should remove values from the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
cache.remove("123");
expect(cache.cache["123"]).toEqual(undefined);
});
================================================
FILE: src/11-record.solution.1.ts
================================================
import { expect, it } from "vitest";
const createCache = () => {
const cache: Record<string, string> = {};
const add = (id: string, value: string) => {
cache[id] = value;
};
const remove = (id: string) => {
delete cache[id];
};
return {
cache,
add,
remove,
};
};
it("Should add values to the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
expect(cache.cache["123"]).toEqual("Matt");
});
it("Should remove values from the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
cache.remove("123");
expect(cache.cache["123"]).toEqual(undefined);
});
================================================
FILE: src/11-record.solution.2.ts
================================================
import { expect, it } from "vitest";
const createCache = () => {
const cache: {
[id: string]: string;
} = {};
const add = (id: string, value: string) => {
cache[id] = value;
};
const remove = (id: string) => {
delete cache[id];
};
return {
cache,
add,
remove,
};
};
it("Should add values to the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
expect(cache.cache["123"]).toEqual("Matt");
});
it("Should remove values from the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
cache.remove("123");
expect(cache.cache["123"]).toEqual(undefined);
});
================================================
FILE: src/11-record.solution.3.ts
================================================
import { expect, it } from "vitest";
interface Cache {
[id: string]: string;
}
const createCache = () => {
const cache: Cache = {};
const add = (id: string, value: string) => {
cache[id] = value;
};
const remove = (id: string) => {
delete cache[id];
};
return {
cache,
add,
remove,
};
};
it("Should add values to the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
expect(cache.cache["123"]).toEqual("Matt");
});
it("Should remove values from the cache", () => {
const cache = createCache();
cache.add("123", "Matt");
cache.remove("123");
expect(cache.cache["123"]).toEqual(undefined);
});
================================================
FILE: src/12-typeof-narrowing.problem.ts
================================================
import { expect, it } from "vitest";
const coerceAmount = (amount: number | { amount: number }) => {};
it("Should return the amount when passed an object", () => {
expect(coerceAmount({ amount: 20 })).toEqual(20);
});
it("Should return the amount when passed a number", () => {
expect(coerceAmount(20)).toEqual(20);
});
================================================
FILE: src/12-typeof-narrowing.solution.ts
================================================
import { expect, it } from "vitest";
const coerceAmount = (amount: number | { amount: number }) => {
if (typeof amount === "number") {
return amount;
}
return amount.amount;
};
it("Should return the amount when passed an object", () => {
expect(coerceAmount({ amount: 20 })).toEqual(20);
});
it("Should return the amount when passed a number", () => {
expect(coerceAmount(20)).toEqual(20);
});
================================================
FILE: src/13-catch-blocks.problem.ts
================================================
import { expect, it } from "vitest";
const tryCatchDemo = (state: "fail" | "succeed") => {
try {
if (state === "fail") {
throw new Error("Failure!");
}
} catch (e) {
return e.message;
}
};
it("Should return the message when it fails", () => {
expect(tryCatchDemo("fail")).toEqual("Failure!");
});
================================================
FILE: src/13-catch-blocks.solution.1.ts
================================================
import { expect, it } from "vitest";
const tryCatchDemo = (state: "fail" | "succeed") => {
try {
if (state === "fail") {
throw new Error("Failure!");
}
} catch (e: any) {
return e.message;
}
};
it("Should return the message when it fails", () => {
expect(tryCatchDemo("fail")).toEqual("Failure!");
});
================================================
FILE: src/13-catch-blocks.solution.2.ts
================================================
import { expect, it } from "vitest";
const tryCatchDemo = (state: "fail" | "succeed") => {
try {
if (state === "fail") {
throw new Error("Failure!");
}
} catch (e) {
return (e as Error).message;
}
};
it("Should return the message when it fails", () => {
expect(tryCatchDemo("fail")).toEqual("Failure!");
});
================================================
FILE: src/13-catch-blocks.solution.3.ts
================================================
import { expect, it } from "vitest";
const tryCatchDemo = (state: "fail" | "succeed") => {
try {
if (state === "fail") {
throw new Error("Failure!");
}
} catch (e) {
if (e instanceof Error) {
return e.message;
}
}
};
it("Should return the message when it fails", () => {
expect(tryCatchDemo("fail")).toEqual("Failure!");
});
================================================
FILE: src/14-extends.problem.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
/**
* Here, the id property is shared between all three
* interfaces. Can you find a way to refactor this to
* make it more DRY?
*/
interface User {
id: string;
firstName: string;
lastName: string;
}
interface Post {
id: string;
title: string;
body: string;
}
interface Comment {
id: string;
comment: string;
}
type tests = [
Expect<Equal<User, { id: string; firstName: string; lastName: string }>>,
Expect<Equal<Post, { id: string; title: string; body: string }>>,
Expect<Equal<Comment, { id: string; comment: string }>>,
];
================================================
FILE: src/14-extends.solution.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
/**
* Here, the id property is shared between all three
* interfaces. Can you find a way to refactor this to
* make it more DRY?
*/
interface Base {
id: string;
}
interface User extends Base {
firstName: string;
lastName: string;
}
interface Post extends Base {
title: string;
body: string;
}
interface Comment extends Base {
comment: string;
}
type tests = [
Expect<Equal<User, { id: string; firstName: string; lastName: string }>>,
Expect<Equal<Post, { id: string; title: string; body: string }>>,
Expect<Equal<Comment, { id: string; comment: string }>>,
];
================================================
FILE: src/15-intersection.problem.ts
================================================
interface User {
id: string;
firstName: string;
lastName: string;
}
interface Post {
id: string;
title: string;
body: string;
}
/**
* How do we type this return statement so it's both
* User AND { posts: Post[] }
*/
export const getDefaultUserAndPosts = (): unknown => {
return {
id: "1",
firstName: "Matt",
lastName: "Pocock",
posts: [
{
id: "1",
title: "How I eat so much cheese",
body: "It's pretty edam difficult",
},
],
};
};
const userAndPosts = getDefaultUserAndPosts();
console.log(userAndPosts.posts[0]);
================================================
FILE: src/15-intersection.solution.ts
================================================
interface User {
id: string;
firstName: string;
lastName: string;
}
interface Post {
id: string;
title: string;
body: string;
}
export const getDefaultUserAndPosts = (): User & { posts: Post[] } => {
return {
id: "1",
firstName: "Matt",
lastName: "Pocock",
posts: [
{
id: "1",
title: "How I eat so much cheese",
body: "It's pretty edam difficult",
},
],
};
};
const userAndPosts = getDefaultUserAndPosts();
console.log(userAndPosts.posts[0]);
================================================
FILE: src/16-omit-and-pick.problem.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
interface User {
id: string;
firstName: string;
lastName: string;
}
/**
* How do we create a new object type with _only_ the
* firstName and lastName properties of User?
*/
type MyType = unknown;
type tests = [Expect<Equal<MyType, { firstName: string; lastName: string }>>];
================================================
FILE: src/16-omit-and-pick.solution.1.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
interface User {
id: string;
firstName: string;
lastName: string;
}
/**
* How do we create a new object type with _only_ the
* firstName and lastName properties of User?
*/
type MyType = Omit<User, "id">;
type tests = [Expect<Equal<MyType, { firstName: string; lastName: string }>>];
================================================
FILE: src/16-omit-and-pick.solution.2.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
interface User {
id: string;
firstName: string;
lastName: string;
}
/**
* How do we create a new object type with _only_ the
* firstName and lastName properties of User?
*/
type MyType = Pick<User, "firstName" | "lastName">;
type tests = [Expect<Equal<MyType, { firstName: string; lastName: string }>>];
================================================
FILE: src/17-function-types.problem.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
/**
* How do we type onFocusChange?
*/
const addListener = (onFocusChange: unknown) => {
window.addEventListener("focus", () => {
onFocusChange(true);
});
window.addEventListener("blur", () => {
onFocusChange(false);
});
};
addListener((isFocused) => {
console.log({ isFocused });
type tests = [Expect<Equal<typeof isFocused, boolean>>];
});
================================================
FILE: src/17-function-types.solution.1.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
/**
* How do we type onFocusChange?
*/
const addListener = (onFocusChange: (isFocused: boolean) => void) => {
window.addEventListener("focus", () => {
onFocusChange(true);
});
window.addEventListener("blur", () => {
onFocusChange(false);
});
};
addListener((isFocused) => {
console.log({ isFocused });
type tests = [Expect<Equal<typeof isFocused, boolean>>];
});
================================================
FILE: src/17-function-types.solution.2.ts
================================================
import { Equal, Expect } from "./helpers/type-utils";
/**
* How do we type onFocusChange?
*/
type FocusListener = (isFocused: boolean) => void;
const addListener = (onFocusChange: FocusListener) => {
window.addEventListener("focus", () => {
onFocusChange(true);
});
window.addEventListener("blur", () => {
onFocusChange(false);
});
};
addListener((isFocused) => {
console.log({ isFocused });
type tests = [Expect<Equal<typeof isFocused, boolean>>];
});
================================================
FILE: src/18-function-types-with-promises.problem.ts
================================================
import { expect, it } from "vitest";
interface User {
id: string;
firstName: string;
lastName: string;
}
const createThenGetUser = async (
createUser: unknown,
getUser: unknown,
): Promise<User> => {
const userId: string = await createUser();
const user = await getUser(userId);
return user;
};
it("Should create the user, then get them", async () => {
const user = await createThenGetUser(
async () => "123",
async (id) => ({
id,
firstName: "Matt",
lastName: "Pocock",
}),
);
expect(user).toEqual({
id: "123",
firstName: "Matt",
lastName: "Pocock",
});
});
================================================
FILE: src/18-function-types-with-promises.solution.ts
================================================
import { expect, it } from "vitest";
interface User {
id: string;
firstName: string;
lastName: string;
}
const createThenGetUser = async (
createUser: () => Promise<string>,
getUser: (id: string) => Promise<User>,
): Promise<User> => {
const userId: string = await createUser();
const user = await getUser(userId);
return user;
};
it("Should create the user, then get them", async () => {
const user = await createThenGetUser(
async () => "123",
async (id) => ({
id,
firstName: "Matt",
lastName: "Pocock",
}),
);
expect(user).toEqual({
id: "123",
firstName: "Matt",
lastName: "Pocock",
});
});
================================================
FILE: src/helpers/type-utils.ts
================================================
export type Expect<T extends true> = T;
export type ExpectTrue<T extends true> = T;
export type ExpectFalse<T extends false> = T;
export type IsTrue<T extends true> = T;
export type IsFalse<T extends false> = T;
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T,
>() => T extends Y ? 1 : 2
? true
: false;
export type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true;
// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
export type IsAny<T> = 0 extends 1 & T ? true : false;
export type NotAny<T> = true extends IsAny<T> ? false : true;
export type Debug<T> = { [K in keyof T]: T[K] };
export type MergeInsertions<T> = T extends object
? { [K in keyof T]: MergeInsertions<T[K]> }
: T;
export type Alike<X, Y> = Equal<MergeInsertions<X>, MergeInsertions<Y>>;
export type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE
? true
: false;
export type ExpectValidArgs<
FUNC extends (...args: any[]) => any,
ARGS extends any[],
> = ARGS extends Parameters<FUNC> ? true : false;
export type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never;
================================================
FILE: tsconfig.json
================================================
{
"extends": "@total-typescript/tsconfig/bundler",
"compilerOptions": {
"noUncheckedIndexedAccess": false,
"verbatimModuleSyntax": false
},
"include": [
"src"
]
}
================================================
FILE: vite.config.mts
================================================
import { defineConfig } from "vitest/config";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
test: {
include: ["src/**/*{problem,solution,explainer}*.{ts,tsx}"],
passWithNoTests: true,
environment: "jsdom",
},
plugins: [tsconfigPaths()],
});
gitextract_69awz6n2/ ├── .github/ │ └── workflows/ │ ├── renovate-checks.yml │ └── section-repos.yml ├── .gitignore ├── .vscode/ │ └── settings.json ├── README.md ├── package.json ├── renovate.json ├── src/ │ ├── 01-number.problem.ts │ ├── 01-number.solution.ts │ ├── 02-object-param.problem.ts │ ├── 02-object-param.solution.1.ts │ ├── 02-object-param.solution.2.ts │ ├── 02-object-param.solution.3.ts │ ├── 03-optional-properties.problem.ts │ ├── 03-optional-properties.solution.ts │ ├── 04-optional-params.problem.ts │ ├── 04-optional-params.solution.ts │ ├── 05-assigning-types-to-variables.problem.ts │ ├── 05-assigning-types-to-variables.solution.ts │ ├── 06-unions.problem.ts │ ├── 06-unions.solution.ts │ ├── 07-arrays.problem.ts │ ├── 07-arrays.solution.1.ts │ ├── 07-arrays.solution.2.ts │ ├── 08-function-return-type-annotations.problem.ts │ ├── 08-function-return-type-annotations.solution.ts │ ├── 09-promises.problem.ts │ ├── 09-promises.solution.1.ts │ ├── 09-promises.solution.2.ts │ ├── 09-promises.solution.3.ts │ ├── 10-set.problem.ts │ ├── 10-set.solution.ts │ ├── 11-record.problem.ts │ ├── 11-record.solution.1.ts │ ├── 11-record.solution.2.ts │ ├── 11-record.solution.3.ts │ ├── 12-typeof-narrowing.problem.ts │ ├── 12-typeof-narrowing.solution.ts │ ├── 13-catch-blocks.problem.ts │ ├── 13-catch-blocks.solution.1.ts │ ├── 13-catch-blocks.solution.2.ts │ ├── 13-catch-blocks.solution.3.ts │ ├── 14-extends.problem.ts │ ├── 14-extends.solution.ts │ ├── 15-intersection.problem.ts │ ├── 15-intersection.solution.ts │ ├── 16-omit-and-pick.problem.ts │ ├── 16-omit-and-pick.solution.1.ts │ ├── 16-omit-and-pick.solution.2.ts │ ├── 17-function-types.problem.ts │ ├── 17-function-types.solution.1.ts │ ├── 17-function-types.solution.2.ts │ ├── 18-function-types-with-promises.problem.ts │ ├── 18-function-types-with-promises.solution.ts │ └── helpers/ │ └── type-utils.ts ├── tsconfig.json └── vite.config.mts
SYMBOL INDEX (66 symbols across 31 files)
FILE: src/02-object-param.solution.2.ts
type AddTwoNumbersArgs (line 3) | type AddTwoNumbersArgs = {
FILE: src/02-object-param.solution.3.ts
type AddTwoNumbersArgs (line 3) | interface AddTwoNumbersArgs {
FILE: src/05-assigning-types-to-variables.problem.ts
type User (line 3) | interface User {
FILE: src/05-assigning-types-to-variables.solution.ts
type User (line 3) | interface User {
FILE: src/06-unions.problem.ts
type User (line 1) | interface User {
FILE: src/06-unions.solution.ts
type User (line 1) | interface User {
FILE: src/07-arrays.problem.ts
type User (line 1) | interface User {
type Post (line 9) | interface Post {
FILE: src/07-arrays.solution.1.ts
type User (line 1) | interface User {
type Post (line 9) | interface Post {
FILE: src/07-arrays.solution.2.ts
type User (line 1) | interface User {
type Post (line 9) | interface Post {
FILE: src/08-function-return-type-annotations.problem.ts
type User (line 3) | interface User {
type Post (line 11) | interface Post {
FILE: src/08-function-return-type-annotations.solution.ts
type User (line 3) | interface User {
type Post (line 11) | interface Post {
FILE: src/09-promises.problem.ts
type LukeSkywalker (line 1) | interface LukeSkywalker {
FILE: src/09-promises.solution.1.ts
type LukeSkywalker (line 1) | interface LukeSkywalker {
FILE: src/09-promises.solution.2.ts
type LukeSkywalker (line 1) | interface LukeSkywalker {
FILE: src/09-promises.solution.3.ts
type LukeSkywalker (line 1) | interface LukeSkywalker {
FILE: src/10-set.problem.ts
type tests (line 22) | type tests = [Expect<Equal<typeof guitaristsAsArray, string[]>>];
FILE: src/10-set.solution.ts
type tests (line 22) | type tests = [Expect<Equal<typeof guitaristsAsArray, string[]>>];
FILE: src/11-record.solution.3.ts
type Cache (line 3) | interface Cache {
FILE: src/14-extends.problem.ts
type User (line 9) | interface User {
type Post (line 15) | interface Post {
type Comment (line 21) | interface Comment {
type tests (line 26) | type tests = [
FILE: src/14-extends.solution.ts
type Base (line 9) | interface Base {
type User (line 13) | interface User extends Base {
type Post (line 18) | interface Post extends Base {
type Comment (line 23) | interface Comment extends Base {
type tests (line 27) | type tests = [
FILE: src/15-intersection.problem.ts
type User (line 1) | interface User {
type Post (line 7) | interface Post {
FILE: src/15-intersection.solution.ts
type User (line 1) | interface User {
type Post (line 7) | interface Post {
FILE: src/16-omit-and-pick.problem.ts
type User (line 3) | interface User {
type MyType (line 14) | type MyType = unknown;
type tests (line 16) | type tests = [Expect<Equal<MyType, { firstName: string; lastName: string...
FILE: src/16-omit-and-pick.solution.1.ts
type User (line 3) | interface User {
type MyType (line 14) | type MyType = Omit<User, "id">;
type tests (line 16) | type tests = [Expect<Equal<MyType, { firstName: string; lastName: string...
FILE: src/16-omit-and-pick.solution.2.ts
type User (line 3) | interface User {
type MyType (line 14) | type MyType = Pick<User, "firstName" | "lastName">;
type tests (line 16) | type tests = [Expect<Equal<MyType, { firstName: string; lastName: string...
FILE: src/17-function-types.problem.ts
type tests (line 19) | type tests = [Expect<Equal<typeof isFocused, boolean>>];
FILE: src/17-function-types.solution.1.ts
type tests (line 19) | type tests = [Expect<Equal<typeof isFocused, boolean>>];
FILE: src/17-function-types.solution.2.ts
type FocusListener (line 6) | type FocusListener = (isFocused: boolean) => void;
type tests (line 21) | type tests = [Expect<Equal<typeof isFocused, boolean>>];
FILE: src/18-function-types-with-promises.problem.ts
type User (line 3) | interface User {
FILE: src/18-function-types-with-promises.solution.ts
type User (line 3) | interface User {
FILE: src/helpers/type-utils.ts
type Expect (line 1) | type Expect<T extends true> = T;
type ExpectTrue (line 2) | type ExpectTrue<T extends true> = T;
type ExpectFalse (line 3) | type ExpectFalse<T extends false> = T;
type IsTrue (line 4) | type IsTrue<T extends true> = T;
type IsFalse (line 5) | type IsFalse<T extends false> = T;
type Equal (line 7) | type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
type NotEqual (line 12) | type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true;
type IsAny (line 15) | type IsAny<T> = 0 extends 1 & T ? true : false;
type NotAny (line 16) | type NotAny<T> = true extends IsAny<T> ? false : true;
type Debug (line 18) | type Debug<T> = { [K in keyof T]: T[K] };
type MergeInsertions (line 19) | type MergeInsertions<T> = T extends object
type Alike (line 23) | type Alike<X, Y> = Equal<MergeInsertions<X>, MergeInsertions<Y>>;
type ExpectExtends (line 25) | type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE
type ExpectValidArgs (line 28) | type ExpectValidArgs<
type UnionToIntersection (line 33) | type UnionToIntersection<U> = (
Condensed preview — 57 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (36K chars).
[
{
"path": ".github/workflows/renovate-checks.yml",
"chars": 933,
"preview": "name: Renovate Checks\non:\n push:\n branches:\n - \"renovate/**\"\n\njobs:\n build:\n runs-on: ubuntu-latest\n ste"
},
{
"path": ".github/workflows/section-repos.yml",
"chars": 644,
"preview": "name: Create Section Repos\non:\n push:\n branches:\n - \"main\"\n\nconcurrency:\n group: ${{ github.workflow }}-${{ gi"
},
{
"path": ".gitignore",
"chars": 85,
"preview": "node_modules\ntsconfig.temp.json\ndist\n*.tsbuildinfo\n*.prompt.*\n.vscode/*.code-snippets"
},
{
"path": ".vscode/settings.json",
"chars": 189,
"preview": "{\n \"typescript.tsdk\": \"node_modules/typescript/lib\",\n \"typescript.enablePromptUseWorkspaceTsdk\": true,\n \"github.copil"
},
{
"path": "README.md",
"chars": 1792,
"preview": "<a href=\"https://totaltypescript.com/tutorials/beginners-typescript\"><img src=\"https://res.cloudinary.com/total-typescri"
},
{
"path": "package.json",
"chars": 2013,
"preview": "{\n \"name\": \"beginners-typescript\",\n \"version\": \"1.0.0\",\n \"type\": \"module\",\n \"main\": \"index.js\",\n \"author\": \"Matt Po"
},
{
"path": "renovate.json",
"chars": 439,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\"config:base\"],\n \"packageRules\": [\n "
},
{
"path": "src/01-number.problem.ts",
"chars": 240,
"preview": "import { expect, it } from \"vitest\";\n\nexport const addTwoNumbers = (a, b) => {\n return a + b;\n};\n\nit(\"Should add the tw"
},
{
"path": "src/01-number.solution.ts",
"chars": 256,
"preview": "import { expect, it } from \"vitest\";\n\nexport const addTwoNumbers = (a: number, b: number) => {\n return a + b;\n};\n\nit(\"S"
},
{
"path": "src/02-object-param.problem.ts",
"chars": 356,
"preview": "import { expect, it } from \"vitest\";\n\nexport const addTwoNumbers = (params) => {\n return params.first + params.second;\n"
},
{
"path": "src/02-object-param.solution.1.ts",
"chars": 391,
"preview": "import { expect, it } from \"vitest\";\n\nexport const addTwoNumbers = (params: { first: number; second: number }) => {\n re"
},
{
"path": "src/02-object-param.solution.2.ts",
"chars": 441,
"preview": "import { expect, it } from \"vitest\";\n\ntype AddTwoNumbersArgs = {\n first: number;\n second: number;\n};\n\nexport const add"
},
{
"path": "src/02-object-param.solution.3.ts",
"chars": 443,
"preview": "import { expect, it } from \"vitest\";\n\ninterface AddTwoNumbersArgs {\n first: number;\n second: number;\n}\n\nexport const a"
},
{
"path": "src/03-optional-properties.problem.ts",
"chars": 513,
"preview": "import { expect, it } from \"vitest\";\n\nexport const getName = (params: { first: string; last: string }) => {\n if (params"
},
{
"path": "src/03-optional-properties.solution.ts",
"chars": 514,
"preview": "import { expect, it } from \"vitest\";\n\nexport const getName = (params: { first: string; last?: string }) => {\n if (param"
},
{
"path": "src/04-optional-params.problem.ts",
"chars": 427,
"preview": "import { expect, it } from \"vitest\";\n\nexport const getName = (first: string, last: string) => {\n if (last) {\n return"
},
{
"path": "src/04-optional-params.solution.ts",
"chars": 428,
"preview": "import { expect, it } from \"vitest\";\n\nexport const getName = (first: string, last?: string) => {\n if (last) {\n retur"
},
{
"path": "src/05-assigning-types-to-variables.problem.ts",
"chars": 411,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: number;\n firstName: string;\n lastName: string;\n isAdmin:"
},
{
"path": "src/05-assigning-types-to-variables.solution.ts",
"chars": 487,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: number;\n firstName: string;\n lastName: string;\n isAdmin:"
},
{
"path": "src/06-unions.problem.ts",
"chars": 348,
"preview": "interface User {\n id: number;\n firstName: string;\n lastName: string;\n /**\n * How do we ensure that role is only on"
},
{
"path": "src/06-unions.solution.ts",
"chars": 374,
"preview": "interface User {\n id: number;\n firstName: string;\n lastName: string;\n /**\n * How do we ensure that role is only on"
},
{
"path": "src/07-arrays.problem.ts",
"chars": 447,
"preview": "interface User {\n id: number;\n firstName: string;\n lastName: string;\n role: \"admin\" | \"user\" | \"super-admin\";\n post"
},
{
"path": "src/07-arrays.solution.1.ts",
"chars": 449,
"preview": "interface User {\n id: number;\n firstName: string;\n lastName: string;\n role: \"admin\" | \"user\" | \"super-admin\";\n post"
},
{
"path": "src/07-arrays.solution.2.ts",
"chars": 454,
"preview": "interface User {\n id: number;\n firstName: string;\n lastName: string;\n role: \"admin\" | \"user\" | \"super-admin\";\n post"
},
{
"path": "src/08-function-return-type-annotations.problem.ts",
"chars": 713,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: number;\n firstName: string;\n lastName: string;\n role: \"a"
},
{
"path": "src/08-function-return-type-annotations.solution.ts",
"chars": 894,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: number;\n firstName: string;\n lastName: string;\n role: \"a"
},
{
"path": "src/09-promises.problem.ts",
"chars": 389,
"preview": "interface LukeSkywalker {\n name: string;\n height: string;\n mass: string;\n hair_color: string;\n skin_color: string;\n"
},
{
"path": "src/09-promises.solution.1.ts",
"chars": 398,
"preview": "interface LukeSkywalker {\n name: string;\n height: string;\n mass: string;\n hair_color: string;\n skin_color: string;\n"
},
{
"path": "src/09-promises.solution.2.ts",
"chars": 391,
"preview": "interface LukeSkywalker {\n name: string;\n height: string;\n mass: string;\n hair_color: string;\n skin_color: string;\n"
},
{
"path": "src/09-promises.solution.3.ts",
"chars": 385,
"preview": "interface LukeSkywalker {\n name: string;\n height: string;\n mass: string;\n hair_color: string;\n skin_color: string;\n"
},
{
"path": "src/10-set.problem.ts",
"chars": 647,
"preview": "import { expect, it } from \"vitest\";\nimport { Equal, Expect } from \"./helpers/type-utils\";\n\nconst guitarists = new Set()"
},
{
"path": "src/10-set.solution.ts",
"chars": 655,
"preview": "import { expect, it } from \"vitest\";\nimport { Equal, Expect } from \"./helpers/type-utils\";\n\nconst guitarists = new Set<s"
},
{
"path": "src/11-record.problem.ts",
"chars": 622,
"preview": "import { expect, it } from \"vitest\";\n\nconst createCache = () => {\n const cache = {};\n\n const add = (id: string, value:"
},
{
"path": "src/11-record.solution.1.ts",
"chars": 646,
"preview": "import { expect, it } from \"vitest\";\n\nconst createCache = () => {\n const cache: Record<string, string> = {};\n\n const a"
},
{
"path": "src/11-record.solution.2.ts",
"chars": 655,
"preview": "import { expect, it } from \"vitest\";\n\nconst createCache = () => {\n const cache: {\n [id: string]: string;\n } = {};\n\n"
},
{
"path": "src/11-record.solution.3.ts",
"chars": 674,
"preview": "import { expect, it } from \"vitest\";\n\ninterface Cache {\n [id: string]: string;\n}\n\nconst createCache = () => {\n const c"
},
{
"path": "src/12-typeof-narrowing.problem.ts",
"chars": 327,
"preview": "import { expect, it } from \"vitest\";\n\nconst coerceAmount = (amount: number | { amount: number }) => {};\n\nit(\"Should retu"
},
{
"path": "src/12-typeof-narrowing.solution.ts",
"chars": 411,
"preview": "import { expect, it } from \"vitest\";\n\nconst coerceAmount = (amount: number | { amount: number }) => {\n if (typeof amoun"
},
{
"path": "src/13-catch-blocks.problem.ts",
"chars": 325,
"preview": "import { expect, it } from \"vitest\";\n\nconst tryCatchDemo = (state: \"fail\" | \"succeed\") => {\n try {\n if (state === \"f"
},
{
"path": "src/13-catch-blocks.solution.1.ts",
"chars": 330,
"preview": "import { expect, it } from \"vitest\";\n\nconst tryCatchDemo = (state: \"fail\" | \"succeed\") => {\n try {\n if (state === \"f"
},
{
"path": "src/13-catch-blocks.solution.2.ts",
"chars": 336,
"preview": "import { expect, it } from \"vitest\";\n\nconst tryCatchDemo = (state: \"fail\" | \"succeed\") => {\n try {\n if (state === \"f"
},
{
"path": "src/13-catch-blocks.solution.3.ts",
"chars": 363,
"preview": "import { expect, it } from \"vitest\";\n\nconst tryCatchDemo = (state: \"fail\" | \"succeed\") => {\n try {\n if (state === \"f"
},
{
"path": "src/14-extends.problem.ts",
"chars": 611,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\n/**\n * Here, the id property is shared between all three\n * inter"
},
{
"path": "src/14-extends.solution.ts",
"chars": 642,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\n/**\n * Here, the id property is shared between all three\n * inter"
},
{
"path": "src/15-intersection.problem.ts",
"chars": 593,
"preview": "interface User {\n id: string;\n firstName: string;\n lastName: string;\n}\n\ninterface Post {\n id: string;\n title: strin"
},
{
"path": "src/15-intersection.solution.ts",
"chars": 519,
"preview": "interface User {\n id: string;\n firstName: string;\n lastName: string;\n}\n\ninterface Post {\n id: string;\n title: strin"
},
{
"path": "src/16-omit-and-pick.problem.ts",
"chars": 342,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\ninterface User {\n id: string;\n firstName: string;\n lastName: s"
},
{
"path": "src/16-omit-and-pick.solution.1.ts",
"chars": 351,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\ninterface User {\n id: string;\n firstName: string;\n lastName: s"
},
{
"path": "src/16-omit-and-pick.solution.2.ts",
"chars": 371,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\ninterface User {\n id: string;\n firstName: string;\n lastName: s"
},
{
"path": "src/17-function-types.problem.ts",
"chars": 422,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\n/**\n * How do we type onFocusChange?\n */\nconst addListener = (onF"
},
{
"path": "src/17-function-types.solution.1.ts",
"chars": 443,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\n/**\n * How do we type onFocusChange?\n */\nconst addListener = (onF"
},
{
"path": "src/17-function-types.solution.2.ts",
"chars": 480,
"preview": "import { Equal, Expect } from \"./helpers/type-utils\";\n\n/**\n * How do we type onFocusChange?\n */\ntype FocusListener = (is"
},
{
"path": "src/18-function-types-with-promises.problem.ts",
"chars": 630,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: string;\n firstName: string;\n lastName: string;\n}\n\nconst c"
},
{
"path": "src/18-function-types-with-promises.solution.ts",
"chars": 666,
"preview": "import { expect, it } from \"vitest\";\n\ninterface User {\n id: string;\n firstName: string;\n lastName: string;\n}\n\nconst c"
},
{
"path": "src/helpers/type-utils.ts",
"chars": 1195,
"preview": "export type Expect<T extends true> = T;\nexport type ExpectTrue<T extends true> = T;\nexport type ExpectFalse<T extends fa"
},
{
"path": "tsconfig.json",
"chars": 184,
"preview": "{\n \"extends\": \"@total-typescript/tsconfig/bundler\",\n \"compilerOptions\": {\n \"noUncheckedIndexedAccess\": false,\n \""
},
{
"path": "vite.config.mts",
"chars": 293,
"preview": "import { defineConfig } from \"vitest/config\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineCon"
}
]
About this extraction
This page contains the full source code of the total-typescript/beginners-typescript-tutorial GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 57 files (29.3 KB), approximately 10.2k tokens, and a symbol index with 66 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.