├── .gitignore ├── scripts ├── setup.ts ├── prepare-stackblitz.js └── exercise.js ├── og-image.png ├── vite.config.ts ├── src ├── 01-number.problem.ts ├── 01-number.solution.ts ├── 12-typeof-narrowing.problem.ts ├── 13-catch-blocks.problem.ts ├── 13-catch-blocks.solution.1.ts ├── 13-catch-blocks.solution.2.ts ├── 16-omit-and-pick.problem.ts ├── 16-omit-and-pick.solution.1.ts ├── 06-unions.problem.ts ├── 13-catch-blocks.solution.3.ts ├── 16-omit-and-pick.solution.2.ts ├── 02-object-param.problem.ts ├── 09-promises.problem.ts ├── 09-promises.solution.2.ts ├── 06-unions.solution.ts ├── 09-promises.solution.1.ts ├── 09-promises.solution.3.ts ├── 12-typeof-narrowing.solution.ts ├── 02-object-param.solution.1.ts ├── 17-function-types.problem.ts ├── 04-optional-params.problem.ts ├── 04-optional-params.solution.ts ├── 05-assigning-types-to-variables.problem.ts ├── 17-function-types.solution.1.ts ├── 02-object-param.solution.2.ts ├── 02-object-param.solution.3.ts ├── 07-arrays.problem.ts ├── 07-arrays.solution.1.ts ├── 07-arrays.solution.2.ts ├── 17-function-types.solution.2.ts ├── 05-assigning-types-to-variables.solution.ts ├── 03-optional-properties.problem.ts ├── 03-optional-properties.solution.ts ├── 15-intersection.solution.ts ├── 10-set.problem.ts ├── 14-extends.problem.ts ├── 10-set.solution.ts ├── 15-intersection.problem.ts ├── 11-record.problem.ts ├── 14-extends.solution.ts ├── 11-record.solution.1.ts ├── 18-function-types-with-promises.problem.ts ├── 11-record.solution.2.ts ├── 18-function-types-with-promises.solution.ts ├── 11-record.solution.3.ts ├── 08-function-return-type-annotations.problem.ts ├── 08-function-return-type-annotations.solution.ts └── helpers │ └── type-utils.ts ├── notes └── FUTURE.md ├── package.json ├── README.md └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode -------------------------------------------------------------------------------- /scripts/setup.ts: -------------------------------------------------------------------------------- 1 | import { fetch } from "cross-fetch"; 2 | 3 | global.fetch = fetch; 4 | -------------------------------------------------------------------------------- /og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phonbopit/beginners-typescript-tutorial/main/og-image.png -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | include: ["src/**/*.ts"], 6 | setupFiles: ["scripts/setup.ts"], 7 | passWithNoTests: true, 8 | teardownTimeout: 5000, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/01-number.problem.ts: -------------------------------------------------------------------------------- 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 | }); 11 | -------------------------------------------------------------------------------- /src/01-number.solution.ts: -------------------------------------------------------------------------------- 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 | }); 11 | -------------------------------------------------------------------------------- /src/12-typeof-narrowing.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const coerceAmount = (amount: number | { amount: number }) => {}; 4 | 5 | it("Should return the amount when passed an object", () => { 6 | expect(coerceAmount({ amount: 20 })).toEqual(20); 7 | }); 8 | 9 | it("Should return the amount when passed a number", () => { 10 | expect(coerceAmount(20)).toEqual(20); 11 | }); 12 | -------------------------------------------------------------------------------- /src/13-catch-blocks.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const tryCatchDemo = (state: "fail" | "succeed") => { 4 | try { 5 | if (state === "fail") { 6 | throw new Error("Failure!"); 7 | } 8 | } catch (e) { 9 | return e.message; 10 | } 11 | }; 12 | 13 | it("Should return the message when it fails", () => { 14 | expect(tryCatchDemo("fail")).toEqual("Failure!"); 15 | }); 16 | -------------------------------------------------------------------------------- /src/13-catch-blocks.solution.1.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const tryCatchDemo = (state: "fail" | "succeed") => { 4 | try { 5 | if (state === "fail") { 6 | throw new Error("Failure!"); 7 | } 8 | } catch (e: any) { 9 | return e.message; 10 | } 11 | }; 12 | 13 | it("Should return the message when it fails", () => { 14 | expect(tryCatchDemo("fail")).toEqual("Failure!"); 15 | }); 16 | -------------------------------------------------------------------------------- /src/13-catch-blocks.solution.2.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const tryCatchDemo = (state: "fail" | "succeed") => { 4 | try { 5 | if (state === "fail") { 6 | throw new Error("Failure!"); 7 | } 8 | } catch (e) { 9 | return (e as Error).message; 10 | } 11 | }; 12 | 13 | it("Should return the message when it fails", () => { 14 | expect(tryCatchDemo("fail")).toEqual("Failure!"); 15 | }); 16 | -------------------------------------------------------------------------------- /src/16-omit-and-pick.problem.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | interface User { 4 | id: string; 5 | firstName: string; 6 | lastName: string; 7 | } 8 | 9 | /** 10 | * How do we create a new object type with _only_ the 11 | * firstName and lastName properties of User? 12 | */ 13 | 14 | type MyType = unknown; 15 | 16 | type tests = [Expect>]; 17 | -------------------------------------------------------------------------------- /src/16-omit-and-pick.solution.1.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | interface User { 4 | id: string; 5 | firstName: string; 6 | lastName: string; 7 | } 8 | 9 | /** 10 | * How do we create a new object type with _only_ the 11 | * firstName and lastName properties of User? 12 | */ 13 | 14 | type MyType = Omit; 15 | 16 | type tests = [Expect>]; 17 | -------------------------------------------------------------------------------- /src/06-unions.problem.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | /** 6 | * How do we ensure that role is only one of: 7 | * - 'admin' 8 | * - 'user' 9 | * - 'super-admin' 10 | */ 11 | role: string; 12 | } 13 | 14 | export const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | // @ts-expect-error 19 | role: "I_SHOULD_NOT_BE_ALLOWED", 20 | }; 21 | -------------------------------------------------------------------------------- /src/13-catch-blocks.solution.3.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const tryCatchDemo = (state: "fail" | "succeed") => { 4 | try { 5 | if (state === "fail") { 6 | throw new Error("Failure!"); 7 | } 8 | } catch (e) { 9 | if (e instanceof Error) { 10 | return e.message; 11 | } 12 | } 13 | }; 14 | 15 | it("Should return the message when it fails", () => { 16 | expect(tryCatchDemo("fail")).toEqual("Failure!"); 17 | }); 18 | -------------------------------------------------------------------------------- /src/16-omit-and-pick.solution.2.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | interface User { 4 | id: string; 5 | firstName: string; 6 | lastName: string; 7 | } 8 | 9 | /** 10 | * How do we create a new object type with _only_ the 11 | * firstName and lastName properties of User? 12 | */ 13 | 14 | type MyType = Pick; 15 | 16 | type tests = [Expect>]; 17 | -------------------------------------------------------------------------------- /src/02-object-param.problem.ts: -------------------------------------------------------------------------------- 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(30); 21 | }); 22 | -------------------------------------------------------------------------------- /src/09-promises.problem.ts: -------------------------------------------------------------------------------- 1 | interface LukeSkywalker { 2 | name: string; 3 | height: string; 4 | mass: string; 5 | hair_color: string; 6 | skin_color: string; 7 | eye_color: string; 8 | birth_year: string; 9 | gender: string; 10 | } 11 | 12 | export const fetchLukeSkywalker = async (): LukeSkywalker => { 13 | const data = await fetch("https://swapi.dev/api/people/1").then((res) => { 14 | return res.json(); 15 | }); 16 | 17 | return data; 18 | }; 19 | -------------------------------------------------------------------------------- /src/09-promises.solution.2.ts: -------------------------------------------------------------------------------- 1 | interface LukeSkywalker { 2 | name: string; 3 | height: string; 4 | mass: string; 5 | hair_color: string; 6 | skin_color: string; 7 | eye_color: string; 8 | birth_year: string; 9 | gender: string; 10 | } 11 | 12 | export const fetchLukeSkywalker = async () => { 13 | const data = await fetch("https://swapi.dev/api/people/1").then((res) => { 14 | return res.json(); 15 | }); 16 | 17 | return data as LukeSkywalker; 18 | }; 19 | -------------------------------------------------------------------------------- /src/06-unions.solution.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | /** 6 | * How do we ensure that role is only one of: 7 | * - 'admin' 8 | * - 'user' 9 | * - 'super-admin' 10 | */ 11 | role: "admin" | "user" | "super-admin"; 12 | } 13 | 14 | export const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | // @ts-expect-error 19 | role: "I_SHOULD_NOT_BE_ALLOWED", 20 | }; 21 | -------------------------------------------------------------------------------- /src/09-promises.solution.1.ts: -------------------------------------------------------------------------------- 1 | interface LukeSkywalker { 2 | name: string; 3 | height: string; 4 | mass: string; 5 | hair_color: string; 6 | skin_color: string; 7 | eye_color: string; 8 | birth_year: string; 9 | gender: string; 10 | } 11 | 12 | export const fetchLukeSkywalker = async (): Promise => { 13 | const data = await fetch("https://swapi.dev/api/people/1").then((res) => { 14 | return res.json(); 15 | }); 16 | 17 | return data; 18 | }; 19 | -------------------------------------------------------------------------------- /src/09-promises.solution.3.ts: -------------------------------------------------------------------------------- 1 | interface LukeSkywalker { 2 | name: string; 3 | height: string; 4 | mass: string; 5 | hair_color: string; 6 | skin_color: string; 7 | eye_color: string; 8 | birth_year: string; 9 | gender: string; 10 | } 11 | 12 | export const fetchLukeSkywalker = async () => { 13 | const data: LukeSkywalker = await fetch( 14 | "https://swapi.dev/api/people/1" 15 | ).then((res) => { 16 | return res.json(); 17 | }); 18 | 19 | return data; 20 | }; 21 | -------------------------------------------------------------------------------- /src/12-typeof-narrowing.solution.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const coerceAmount = (amount: number | { amount: number }) => { 4 | if (typeof amount === "number") { 5 | return amount; 6 | } 7 | return amount.amount; 8 | }; 9 | 10 | it("Should return the amount when passed an object", () => { 11 | expect(coerceAmount({ amount: 20 })).toEqual(20); 12 | }); 13 | 14 | it("Should return the amount when passed a number", () => { 15 | expect(coerceAmount(20)).toEqual(20); 16 | }); 17 | -------------------------------------------------------------------------------- /src/02-object-param.solution.1.ts: -------------------------------------------------------------------------------- 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(30); 21 | }); 22 | -------------------------------------------------------------------------------- /src/17-function-types.problem.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | /** 4 | * How do we type onFocusChange? 5 | */ 6 | const addListener = (onFocusChange: unknown) => { 7 | window.addEventListener("focus", () => { 8 | onFocusChange(true); 9 | }); 10 | 11 | window.addEventListener("blur", () => { 12 | onFocusChange(false); 13 | }); 14 | }; 15 | 16 | addListener((isFocused) => { 17 | console.log({ isFocused }); 18 | 19 | type tests = [Expect>]; 20 | }); 21 | -------------------------------------------------------------------------------- /src/04-optional-params.problem.ts: -------------------------------------------------------------------------------- 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 | }); 21 | -------------------------------------------------------------------------------- /src/04-optional-params.solution.ts: -------------------------------------------------------------------------------- 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 | }); 21 | -------------------------------------------------------------------------------- /src/05-assigning-types-to-variables.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: number; 5 | firstName: string; 6 | lastName: string; 7 | isAdmin: boolean; 8 | } 9 | 10 | /** 11 | * How do we ensure that defaultUser is of type User 12 | * at THIS LINE - not further down in the code? 13 | */ 14 | const defaultUser = {}; 15 | 16 | const getUserId = (user: User) => { 17 | return user.id; 18 | }; 19 | 20 | it("Should get the user id", () => { 21 | expect(getUserId(defaultUser)).toEqual(1); 22 | }); 23 | -------------------------------------------------------------------------------- /src/17-function-types.solution.1.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | /** 4 | * How do we type onFocusChange? 5 | */ 6 | const addListener = (onFocusChange: (isFocused: boolean) => void) => { 7 | window.addEventListener("focus", () => { 8 | onFocusChange(true); 9 | }); 10 | 11 | window.addEventListener("blur", () => { 12 | onFocusChange(false); 13 | }); 14 | }; 15 | 16 | addListener((isFocused) => { 17 | console.log({ isFocused }); 18 | 19 | type tests = [Expect>]; 20 | }); 21 | -------------------------------------------------------------------------------- /src/02-object-param.solution.2.ts: -------------------------------------------------------------------------------- 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(30); 26 | }); 27 | -------------------------------------------------------------------------------- /src/02-object-param.solution.3.ts: -------------------------------------------------------------------------------- 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(30); 26 | }); 27 | -------------------------------------------------------------------------------- /src/07-arrays.problem.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | role: "admin" | "user" | "super-admin"; 6 | posts: Post; 7 | } 8 | 9 | interface Post { 10 | id: number; 11 | title: string; 12 | } 13 | 14 | export const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | role: "admin", 19 | posts: [ 20 | { 21 | id: 1, 22 | title: "How I eat so much cheese", 23 | }, 24 | { 25 | id: 2, 26 | title: "Why I don't eat more vegetables", 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /src/07-arrays.solution.1.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | role: "admin" | "user" | "super-admin"; 6 | posts: Post[]; 7 | } 8 | 9 | interface Post { 10 | id: number; 11 | title: string; 12 | } 13 | 14 | export const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | role: "admin", 19 | posts: [ 20 | { 21 | id: 1, 22 | title: "How I eat so much cheese", 23 | }, 24 | { 25 | id: 2, 26 | title: "Why I don't eat more vegetables", 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /src/07-arrays.solution.2.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | role: "admin" | "user" | "super-admin"; 6 | posts: Array; 7 | } 8 | 9 | interface Post { 10 | id: number; 11 | title: string; 12 | } 13 | 14 | export const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | role: "admin", 19 | posts: [ 20 | { 21 | id: 1, 22 | title: "How I eat so much cheese", 23 | }, 24 | { 25 | id: 2, 26 | title: "Why I don't eat more vegetables", 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /src/17-function-types.solution.2.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | /** 4 | * How do we type onFocusChange? 5 | */ 6 | type FocusListener = (isFocused: boolean) => void; 7 | 8 | const addListener = (onFocusChange: FocusListener) => { 9 | window.addEventListener("focus", () => { 10 | onFocusChange(true); 11 | }); 12 | 13 | window.addEventListener("blur", () => { 14 | onFocusChange(false); 15 | }); 16 | }; 17 | 18 | addListener((isFocused) => { 19 | console.log({ isFocused }); 20 | 21 | type tests = [Expect>]; 22 | }); 23 | -------------------------------------------------------------------------------- /src/05-assigning-types-to-variables.solution.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: number; 5 | firstName: string; 6 | lastName: string; 7 | isAdmin: boolean; 8 | } 9 | 10 | /** 11 | * How do we ensure that defaultUser is of type User 12 | * at THIS LINE - not further down in the code? 13 | */ 14 | const defaultUser: User = { 15 | id: 1, 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | isAdmin: true, 19 | }; 20 | 21 | const getUserId = (user: User) => { 22 | return user.id; 23 | }; 24 | 25 | it("Should get the user id", () => { 26 | expect(getUserId(defaultUser)).toEqual(1); 27 | }); 28 | -------------------------------------------------------------------------------- /src/03-optional-properties.problem.ts: -------------------------------------------------------------------------------- 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 | }); 26 | -------------------------------------------------------------------------------- /src/03-optional-properties.solution.ts: -------------------------------------------------------------------------------- 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 | }); 26 | -------------------------------------------------------------------------------- /src/15-intersection.solution.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: string; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | 7 | interface Post { 8 | id: string; 9 | title: string; 10 | body: string; 11 | } 12 | 13 | export const getDefaultUserAndPosts = (): User & { posts: Post[] } => { 14 | return { 15 | id: "1", 16 | firstName: "Matt", 17 | lastName: "Pocock", 18 | posts: [ 19 | { 20 | id: "1", 21 | title: "How I eat so much cheese", 22 | body: "It's pretty edam difficult", 23 | }, 24 | ], 25 | }; 26 | }; 27 | 28 | const userAndPosts = getDefaultUserAndPosts(); 29 | 30 | console.log(userAndPosts.posts[0]); 31 | -------------------------------------------------------------------------------- /notes/FUTURE.md: -------------------------------------------------------------------------------- 1 | ## Things to add 2 | 3 | - classes 4 | - Omit, Partial, Pick, Required, Record (object-based utility types) 5 | - ReadonlyArray 6 | 7 | ## Things NOT to add 8 | 9 | These things are out-of-scope, or will be included in other modules 10 | 11 | - limits of typeof narrowing (unknown and object) 12 | - Type predicates 13 | - Using external libraries and @types 14 | - discriminated unions 15 | - narrowing on discriminated unions 16 | - Template literals 17 | - Branded types 18 | - Readonly properties on objects 19 | - Index signatures 20 | - `keyof` 21 | - Readonly, Exclude, Extract (union-based or readonly-based utility types) 22 | - let vs const vs as const 23 | - Function overloads 24 | - typeof 25 | - Generics in functions 26 | -------------------------------------------------------------------------------- /src/10-set.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | import { Equal, Expect } from "./helpers/type-utils"; 3 | 4 | const guitarists = new Set(); 5 | 6 | guitarists.add("Jimi Hendrix"); 7 | guitarists.add("Eric Clapton"); 8 | 9 | it("Should contain Jimi and Eric", () => { 10 | expect(guitarists.has("Jimi Hendrix")).toEqual(true); 11 | expect(guitarists.has("Eric Clapton")).toEqual(true); 12 | }); 13 | 14 | it("Should give a type error when you try to pass a non-string", () => { 15 | // @ts-expect-error 16 | guitarists.add(2); 17 | }); 18 | 19 | it("Should be typed as an array of strings", () => { 20 | const guitaristsAsArray = Array.from(guitarists); 21 | 22 | type tests = [Expect>]; 23 | }); 24 | -------------------------------------------------------------------------------- /src/14-extends.problem.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | /** 4 | * Here, the id property is shared between all three 5 | * interfaces. Can you find a way to refactor this to 6 | * make it more DRY? 7 | */ 8 | 9 | interface User { 10 | id: string; 11 | firstName: string; 12 | lastName: string; 13 | } 14 | 15 | interface Post { 16 | id: string; 17 | title: string; 18 | body: string; 19 | } 20 | 21 | interface Comment { 22 | id: string; 23 | comment: string; 24 | } 25 | 26 | type tests = [ 27 | Expect>, 28 | Expect>, 29 | Expect>, 30 | ]; 31 | -------------------------------------------------------------------------------- /src/10-set.solution.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | import { Equal, Expect } from "./helpers/type-utils"; 3 | 4 | const guitarists = new Set(); 5 | 6 | guitarists.add("Jimi Hendrix"); 7 | guitarists.add("Eric Clapton"); 8 | 9 | it("Should contain Jimi and Eric", () => { 10 | expect(guitarists.has("Jimi Hendrix")).toEqual(true); 11 | expect(guitarists.has("Eric Clapton")).toEqual(true); 12 | }); 13 | 14 | it("Should give a type error when you try to pass a non-string", () => { 15 | // @ts-expect-error 16 | guitarists.add(2); 17 | }); 18 | 19 | it("Should be typed as an array of strings", () => { 20 | const guitaristsAsArray = Array.from(guitarists); 21 | 22 | type tests = [Expect>]; 23 | }); 24 | -------------------------------------------------------------------------------- /src/15-intersection.problem.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: string; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | 7 | interface Post { 8 | id: string; 9 | title: string; 10 | body: string; 11 | } 12 | 13 | /** 14 | * How do we type this return statement so it's both 15 | * User AND { posts: Post[] } 16 | */ 17 | export const getDefaultUserAndPosts = (): unknown => { 18 | return { 19 | id: "1", 20 | firstName: "Matt", 21 | lastName: "Pocock", 22 | posts: [ 23 | { 24 | id: "1", 25 | title: "How I eat so much cheese", 26 | body: "It's pretty edam difficult", 27 | }, 28 | ], 29 | }; 30 | }; 31 | 32 | const userAndPosts = getDefaultUserAndPosts(); 33 | 34 | console.log(userAndPosts.posts[0]); 35 | -------------------------------------------------------------------------------- /src/11-record.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const createCache = () => { 4 | const cache = {}; 5 | 6 | const add = (id: string, value: string) => { 7 | cache[id] = value; 8 | }; 9 | 10 | const remove = (id: string) => { 11 | delete cache[id]; 12 | }; 13 | 14 | return { 15 | cache, 16 | add, 17 | remove, 18 | }; 19 | }; 20 | 21 | it("Should add values to the cache", () => { 22 | const cache = createCache(); 23 | 24 | cache.add("123", "Matt"); 25 | 26 | expect(cache.cache["123"]).toEqual("Matt"); 27 | }); 28 | 29 | it("Should remove values to the cache", () => { 30 | const cache = createCache(); 31 | 32 | cache.add("123", "Matt"); 33 | cache.remove("123"); 34 | 35 | expect(cache.cache["123"]).toEqual(undefined); 36 | }); 37 | -------------------------------------------------------------------------------- /src/14-extends.solution.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "./helpers/type-utils"; 2 | 3 | /** 4 | * Here, the id property is shared between all three 5 | * interfaces. Can you find a way to refactor this to 6 | * make it more DRY? 7 | */ 8 | 9 | interface Base { 10 | id: string; 11 | } 12 | 13 | interface User extends Base { 14 | firstName: string; 15 | lastName: string; 16 | } 17 | 18 | interface Post extends Base { 19 | title: string; 20 | body: string; 21 | } 22 | 23 | interface Comment extends Base { 24 | comment: string; 25 | } 26 | 27 | type tests = [ 28 | Expect>, 29 | Expect>, 30 | Expect>, 31 | ]; 32 | -------------------------------------------------------------------------------- /src/11-record.solution.1.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const createCache = () => { 4 | const cache: Record = {}; 5 | 6 | const add = (id: string, value: string) => { 7 | cache[id] = value; 8 | }; 9 | 10 | const remove = (id: string) => { 11 | delete cache[id]; 12 | }; 13 | 14 | return { 15 | cache, 16 | add, 17 | remove, 18 | }; 19 | }; 20 | 21 | it("Should add values to the cache", () => { 22 | const cache = createCache(); 23 | 24 | cache.add("123", "Matt"); 25 | 26 | expect(cache.cache["123"]).toEqual("Matt"); 27 | }); 28 | 29 | it("Should remove values to the cache", () => { 30 | const cache = createCache(); 31 | 32 | cache.add("123", "Matt"); 33 | cache.remove("123"); 34 | 35 | expect(cache.cache["123"]).toEqual(undefined); 36 | }); 37 | -------------------------------------------------------------------------------- /src/18-function-types-with-promises.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: string; 5 | firstName: string; 6 | lastName: string; 7 | } 8 | 9 | const createThenGetUser = async ( 10 | createUser: unknown, 11 | getUser: unknown, 12 | ): Promise => { 13 | const userId: string = await createUser(); 14 | 15 | const user = await getUser(userId); 16 | 17 | return user; 18 | }; 19 | 20 | it("Should create the user, then get them", async () => { 21 | const user = await createThenGetUser( 22 | async () => "123", 23 | async (id) => ({ 24 | id, 25 | firstName: "Matt", 26 | lastName: "Pocock", 27 | }), 28 | ); 29 | 30 | expect(user).toEqual({ 31 | id: "123", 32 | firstName: "Matt", 33 | lastName: "Pocock", 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/11-record.solution.2.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | const createCache = () => { 4 | const cache: { 5 | [id: string]: string; 6 | } = {}; 7 | 8 | const add = (id: string, value: string) => { 9 | cache[id] = value; 10 | }; 11 | 12 | const remove = (id: string) => { 13 | delete cache[id]; 14 | }; 15 | 16 | return { 17 | cache, 18 | add, 19 | remove, 20 | }; 21 | }; 22 | 23 | it("Should add values to the cache", () => { 24 | const cache = createCache(); 25 | 26 | cache.add("123", "Matt"); 27 | 28 | expect(cache.cache["123"]).toEqual("Matt"); 29 | }); 30 | 31 | it("Should remove values to the cache", () => { 32 | const cache = createCache(); 33 | 34 | cache.add("123", "Matt"); 35 | cache.remove("123"); 36 | 37 | expect(cache.cache["123"]).toEqual(undefined); 38 | }); 39 | -------------------------------------------------------------------------------- /src/18-function-types-with-promises.solution.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: string; 5 | firstName: string; 6 | lastName: string; 7 | } 8 | 9 | const createThenGetUser = async ( 10 | createUser: () => Promise, 11 | getUser: (id: string) => Promise, 12 | ): Promise => { 13 | const userId: string = await createUser(); 14 | 15 | const user = await getUser(userId); 16 | 17 | return user; 18 | }; 19 | 20 | it("Should create the user, then get them", async () => { 21 | const user = await createThenGetUser( 22 | async () => "123", 23 | async (id) => ({ 24 | id, 25 | firstName: "Matt", 26 | lastName: "Pocock", 27 | }), 28 | ); 29 | 30 | expect(user).toEqual({ 31 | id: "123", 32 | firstName: "Matt", 33 | lastName: "Pocock", 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/11-record.solution.3.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface Cache { 4 | [id: string]: string; 5 | } 6 | 7 | const createCache = () => { 8 | const cache: Cache = {}; 9 | 10 | const add = (id: string, value: string) => { 11 | cache[id] = value; 12 | }; 13 | 14 | const remove = (id: string) => { 15 | delete cache[id]; 16 | }; 17 | 18 | return { 19 | cache, 20 | add, 21 | remove, 22 | }; 23 | }; 24 | 25 | it("Should add values to the cache", () => { 26 | const cache = createCache(); 27 | 28 | cache.add("123", "Matt"); 29 | 30 | expect(cache.cache["123"]).toEqual("Matt"); 31 | }); 32 | 33 | it("Should remove values to the cache", () => { 34 | const cache = createCache(); 35 | 36 | cache.add("123", "Matt"); 37 | cache.remove("123"); 38 | 39 | expect(cache.cache["123"]).toEqual(undefined); 40 | }); 41 | -------------------------------------------------------------------------------- /src/08-function-return-type-annotations.problem.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: number; 5 | firstName: string; 6 | lastName: string; 7 | role: "admin" | "user" | "super-admin"; 8 | posts: Array; 9 | } 10 | 11 | interface Post { 12 | id: number; 13 | title: string; 14 | } 15 | 16 | /** 17 | * How do we ensure that makeUser ALWAYS 18 | * returns a user? 19 | */ 20 | const makeUser = () => { 21 | return {}; 22 | }; 23 | 24 | it("Should return a valid user", () => { 25 | const user = makeUser(); 26 | 27 | expect(user.id).toBeTypeOf("number"); 28 | expect(user.firstName).toBeTypeOf("string"); 29 | expect(user.lastName).toBeTypeOf("string"); 30 | expect(user.role).to.be.oneOf(["super-admin", "admin", "user"]); 31 | 32 | expect(user.posts[0].id).toBeTypeOf("number"); 33 | expect(user.posts[0].title).toBeTypeOf("string"); 34 | }); 35 | -------------------------------------------------------------------------------- /scripts/prepare-stackblitz.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | /** 5 | * Adds a bunch of scripts, like e-01, e-02 to package.json 6 | * so that StackBlitz can run them programmatically via URL 7 | * commands 8 | */ 9 | 10 | const packageJsonPath = path.resolve(__dirname, "../package.json"); 11 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); 12 | const exercises = fs.readdirSync(path.resolve(__dirname, "../src")); 13 | const exerciseFiles = exercises.filter((exercise) => 14 | exercise.includes(".problem."), 15 | ); 16 | const exerciseNames = exerciseFiles.map((exercise) => exercise.split("-")[0]); 17 | 18 | const newPackageJson = Object.assign({}, packageJson); 19 | 20 | newPackageJson.scripts = { 21 | ...packageJson.scripts, 22 | }; 23 | 24 | exerciseNames.forEach((exercise) => { 25 | newPackageJson.scripts[`e-${exercise}`] = `yarn exercise ${exercise}`; 26 | newPackageJson.scripts[`s-${exercise}`] = `yarn solution ${exercise}`; 27 | }); 28 | 29 | fs.writeFileSync(packageJsonPath, JSON.stringify(newPackageJson, null, 2)); 30 | -------------------------------------------------------------------------------- /src/08-function-return-type-annotations.solution.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from "vitest"; 2 | 3 | interface User { 4 | id: number; 5 | firstName: string; 6 | lastName: string; 7 | role: "admin" | "user" | "super-admin"; 8 | posts: Array; 9 | } 10 | 11 | interface Post { 12 | id: number; 13 | title: string; 14 | } 15 | 16 | /** 17 | * How do we ensure that makeUser ALWAYS 18 | * returns a user? 19 | */ 20 | const makeUser = (): User => { 21 | return { 22 | id: 1, 23 | firstName: "Matt", 24 | lastName: "Pocock", 25 | role: "admin", 26 | posts: [ 27 | { 28 | id: 1, 29 | title: "How I eat so much cheese", 30 | }, 31 | ], 32 | }; 33 | }; 34 | 35 | it("Should return a valid user", () => { 36 | const user = makeUser(); 37 | 38 | expect(user.id).toBeTypeOf("number"); 39 | expect(user.firstName).toBeTypeOf("string"); 40 | expect(user.lastName).toBeTypeOf("string"); 41 | expect(user.role).to.be.oneOf(["super-admin", "admin", "user"]); 42 | 43 | expect(user.posts[0].id).toBeTypeOf("number"); 44 | expect(user.posts[0].title).toBeTypeOf("string"); 45 | }); 46 | -------------------------------------------------------------------------------- /src/helpers/type-utils.ts: -------------------------------------------------------------------------------- 1 | export type Expect = T; 2 | export type ExpectTrue = T; 3 | export type ExpectFalse = T; 4 | export type IsTrue = T; 5 | export type IsFalse = T; 6 | 7 | export type Equal = (() => T extends X ? 1 : 2) extends < 8 | T, 9 | >() => T extends Y ? 1 : 2 10 | ? true 11 | : false; 12 | export type NotEqual = true extends Equal ? false : true; 13 | 14 | // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360 15 | export type IsAny = 0 extends 1 & T ? true : false; 16 | export type NotAny = true extends IsAny ? false : true; 17 | 18 | export type Debug = { [K in keyof T]: T[K] }; 19 | export type MergeInsertions = T extends object 20 | ? { [K in keyof T]: MergeInsertions } 21 | : T; 22 | 23 | export type Alike = Equal, MergeInsertions>; 24 | 25 | export type ExpectExtends = 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 ? true : false; 32 | 33 | export type UnionToIntersection = ( 34 | U extends any ? (k: U) => void : never 35 | ) extends (k: infer I) => void 36 | ? I 37 | : never; 38 | -------------------------------------------------------------------------------- /scripts/exercise.js: -------------------------------------------------------------------------------- 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 pathIndicator = ".problem."; 19 | 20 | if (process.env.SOLUTION) { 21 | pathIndicator = ".solution."; 22 | } 23 | 24 | const exercisePath = allExercises.find( 25 | (exercisePath) => 26 | exercisePath.startsWith(exercise) && exercisePath.includes(pathIndicator), 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 | const fileContents = fs.readFileSync(exerciseFile, "utf8"); 39 | 40 | const containsVitest = fileContents.includes("vitest"); 41 | try { 42 | console.clear(); 43 | if (containsVitest) { 44 | console.log("Running tests..."); 45 | execSync(`vitest run "${exerciseFile}" --passWithNoTests`, { 46 | stdio: "inherit", 47 | }); 48 | } 49 | console.log("Checking types..."); 50 | execSync(`tsc "${exerciseFile}" --noEmit --strict`, { 51 | stdio: "inherit", 52 | }); 53 | console.log("Typecheck complete. You finished the exercise!"); 54 | } catch (e) { 55 | console.log("Failed. Try again!"); 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beginners-typescript", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Matt Pocock ", 6 | "license": "GPL-3.0", 7 | "devDependencies": { 8 | "@types/node": "^18.6.5", 9 | "chokidar": "^3.5.3", 10 | "cross-fetch": "^3.1.5", 11 | "typescript": "^4.8.3", 12 | "vitest": "^0.21.1" 13 | }, 14 | "scripts": { 15 | "exercise": "node scripts/exercise.js", 16 | "e": "yarn run exercise", 17 | "solution": "SOLUTION=true node scripts/exercise.js", 18 | "s": "yarn run solution", 19 | "prepare": "node scripts/prepare-stackblitz.js", 20 | "e-01": "yarn exercise 01", 21 | "s-01": "yarn solution 01", 22 | "e-02": "yarn exercise 02", 23 | "s-02": "yarn solution 02", 24 | "e-03": "yarn exercise 03", 25 | "s-03": "yarn solution 03", 26 | "e-04": "yarn exercise 04", 27 | "s-04": "yarn solution 04", 28 | "e-05": "yarn exercise 05", 29 | "s-05": "yarn solution 05", 30 | "e-06": "yarn exercise 06", 31 | "s-06": "yarn solution 06", 32 | "e-07": "yarn exercise 07", 33 | "s-07": "yarn solution 07", 34 | "e-08": "yarn exercise 08", 35 | "s-08": "yarn solution 08", 36 | "e-09": "yarn exercise 09", 37 | "s-09": "yarn solution 09", 38 | "e-10": "yarn exercise 10", 39 | "s-10": "yarn solution 10", 40 | "e-11": "yarn exercise 11", 41 | "s-11": "yarn solution 11", 42 | "e-12": "yarn exercise 12", 43 | "s-12": "yarn solution 12", 44 | "e-13": "yarn exercise 13", 45 | "s-13": "yarn solution 13", 46 | "e-14": "yarn exercise 14", 47 | "s-14": "yarn solution 14", 48 | "e-15": "yarn exercise 15", 49 | "s-15": "yarn solution 15", 50 | "e-16": "yarn exercise 16", 51 | "s-16": "yarn solution 16", 52 | "e-17": "yarn exercise 17", 53 | "s-17": "yarn solution 17", 54 | "e-18": "yarn exercise 18", 55 | "s-18": "yarn solution 18" 56 | }, 57 | "dependencies": { 58 | "@types/express": "^4.17.13", 59 | "express": "^4.18.1", 60 | "zod": "^3.17.10" 61 | } 62 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | beginner typescript tutorial 2 | 3 | ## Quickstart 4 | 5 | Clone this repo or [open in Gitpod](https://gitpod.io/#https://github.com/total-typescript/beginners-typescript). 6 | 7 | ```sh 8 | # Installs all dependencies 9 | npm install 10 | 11 | # Starts the first exercise 12 | npm run exercise 01 13 | 14 | # Runs linting and tests on the solution 15 | npm run solution 01 16 | ``` 17 | 18 | ## Video Walkthrough 19 | 20 | I walked through the first few exercises on [VSCode's live stream](https://www.youtube.com/watch?v=p6dO9u0M7MQ)! The plan for these exercises is to develop them into a full workshop, and then bundle them into the full video course - [Total TypeScript](https://totaltypescript.com). 21 | 22 | ## How to take the course 23 | 24 | You'll notice that the course is split into exercises. Each exercise is split into a `*.problem.ts` and a `*.solution.ts`. 25 | 26 | To take an exercise: 27 | 28 | 1. Go into `*.problem.ts` 29 | 2. Run `npm run exercise 01`, where `01` is the number of the exercise you're on. 30 | 31 | The `exercise` script will run TypeScript typechecks and a test suite on the exercise. 32 | 33 | 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: 34 | 35 | 1. Check out [TypeScript's docs](https://www.typescriptlang.org/docs/handbook/intro.html) 36 | 2. Try to find something that looks relevant. 37 | 3. Give it a go to see if it solves the problem. 38 | 39 | You'll know if you've succeeded because the tests will pass. 40 | 41 | **If you succeed**, 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! 42 | 43 | You can run `npm run solution 01` to run the tests and typechecking on the solution. 44 | 45 | ## Acknowledgements 46 | 47 | 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). 48 | 49 | ## Reference 50 | 51 | ### `npm run exercise 01` 52 | 53 | Alias: `npm run e 01` 54 | 55 | Run the corresponding `*.problem.ts` file. 56 | 57 | ### `npm run solution 01` 58 | 59 | Alias: `npm run s 01` 60 | 61 | Run the corresponding `*.solution.ts` file. If there are multiple, it runs only the first one. 62 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": [ 4 | "vitest/importMeta" 5 | ], 6 | /* Visit https://aka.ms/tsconfig to read more about this file */ 7 | /* Projects */ 8 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 9 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 10 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 11 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 12 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 13 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 14 | /* Language and Environment */ 15 | "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 16 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 17 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 18 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 23 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 26 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 27 | /* Modules */ 28 | "module": "ES2022", /* Specify what module code is generated. */ 29 | "moduleResolution": "node", 30 | // "rootDir": "./", /* Specify the root folder within your source files. */ 31 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 32 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 36 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 38 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 39 | // "resolveJsonModule": true, /* Enable importing .json files. */ 40 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 41 | /* JavaScript Support */ 42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | /* Emit */ 46 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 47 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 48 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 49 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 50 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 51 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 52 | // "removeComments": true, /* Disable emitting comments. */ 53 | "noEmit": true, /* Disable emitting files from a compilation. */ 54 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 55 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 56 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 57 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 60 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 61 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 62 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 63 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 64 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 65 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 66 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 67 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 68 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 69 | /* Interop Constraints */ 70 | "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | /* Type Checking */ 76 | "strict": true, /* Enable all strict type-checking options. */ 77 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 78 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 79 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 80 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 81 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 82 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 83 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 84 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 85 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 86 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 87 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 88 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 89 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 90 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 91 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 92 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 93 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 94 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 95 | /* Completeness */ 96 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 97 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 98 | }, 99 | "include": [ 100 | "src" 101 | ] 102 | } --------------------------------------------------------------------------------