diff --git a/packages/notes-ts-fundamentals-v4/src/03-variables-and-values.ts b/packages/notes-ts-fundamentals-v4/src/03-variables-and-values.ts index fd48b9e6e..e28013aac 100644 --- a/packages/notes-ts-fundamentals-v4/src/03-variables-and-values.ts +++ b/packages/notes-ts-fundamentals-v4/src/03-variables-and-values.ts @@ -1,69 +1,59 @@ //* Variable Declarations & Inference let temperature = 6 //! inference -/* -// temperature = "warm" //! type-checking + +temperature = 'warm' //! type-checking // const humidity = 79 //! literal type //* A type as a set of allowed values -/* -// temperature = 23 //✔️ (1) - re-assignability of a let -// temperature = humidity; //! (2) - type-checking -// humidity = temperature; //! (3) - number is not of type `79` -// humidity = 79; //✔️ (4) - 79 is of type `79` -// humidity = 78; //! (5) - 78 is not of type `79` - - -/* -// let temp2 = 19; //! temp2's type is { all numbers } -// let humid2 = 79 as const; //! humidity's type is { 79 } -// temp2 = 23; //! Is each member in { 23 } also in { all numbers }? -// temp2 = humid2; //! Is each member in { 79 } also in { all numbers }? -// humid2 = temp2; //! Is each member in { all numbers } also in { 79 }? -// humid2 = 79; //! Is each member in { 79 } also in { 79 } -// humid2 = 78; //! Is each member in { 78 } also in { 79 } + +temperature = 23 //✔️ (1) - re-assignability of a let +temperature = humidity //! (2) - type-checking +humidity = temperature //! (3) - number is not of type `79` +humidity = 79 //✔️ (4) - 79 is of type `79` +humidity = 78 //! (5) - 78 is not of type `79` + +let temp2 = 19 //! temp2's type is { all numbers } +let humid2 = 79 as const //! humidity's type is { 79 } +temp2 = 23 //! Is each member in { 23 } also in { all numbers }? +temp2 = humid2 //! Is each member in { 79 } also in { all numbers }? +humid2 = temp2 //! Is each member in { all numbers } also in { 79 }? +humid2 = 79 //! Is each member in { 79 } also in { 79 } +humid2 = 78 //! Is each member in { 78 } also in { 79 } //* Implicit `any` and type annotations -/* -// between 500 and 1000 -// export const RANDOM_WAIT_TIME = -// Math.round(Math.random() * 500) + 500 -// let startTime = new Date() -// let endTime +// between 500 and 1000 +export const RANDOM_WAIT_TIME = Math.round(Math.random() * 500) + 500 -// setTimeout(() => { -// endTime = 0 -// endTime = new Date() -// }, RANDOM_WAIT_TIME) +let startTime = new Date() +let endTime: Date +setTimeout(() => { + // endTime = 0 //! Type 'number' is not assignable to type 'Date' + endTime = new Date() +}, RANDOM_WAIT_TIME) //* Type Casting -/* -// let frontEndMastersFounding = new Date("Jan 1, 2012") -// let date1 = frontEndMastersFounding -// let date2 = frontEndMastersFounding as any; - -/* -// const humid3 = 79 as number; //✔️ is 79 a number? If so, this is safe! +let frontEndMastersFounding = new Date('Jan 1, 2012') +let date1 = frontEndMastersFounding +let date2 = frontEndMastersFounding as any -/* -// let date3 = "oops" as any as Date //! TypeScript thinks this is a Date now, but it's really a string -// date3.toISOString() //! what do we think will happen when we run this? 💥 +const humid3 = 79 as number //✔️ is 79 a number? If so, this is safe! -/* -// let date4 = "oops" as Date +let date3 = 'oops' as unknown as Date //! TypeScript thinks this is a Date now, but it's really a string +date3.toISOString() //! what do we think will happen when we run this? 💥 +let date4 = 'oops' as Date //! TypeScript thinks this is a Date now, but it's really a string //! Function arguments and return values +function add(a: number, b: number): number { + return a + b // strings? numbers? a mix? +} -// function add(a, b) { -// return a + b // strings? numbers? a mix? -// } - -// const result = add(3, "4") -// const p = new Promise(result); +const result = add(3, 4) +const p = new Promise(result) /**/ diff --git a/packages/notes-ts-fundamentals-v4/src/04-objects-arrays-and-tuples.ts b/packages/notes-ts-fundamentals-v4/src/04-objects-arrays-and-tuples.ts index af00ccf19..cad11ca1d 100644 --- a/packages/notes-ts-fundamentals-v4/src/04-objects-arrays-and-tuples.ts +++ b/packages/notes-ts-fundamentals-v4/src/04-objects-arrays-and-tuples.ts @@ -1,130 +1,154 @@ //* Objects +const myCar = { + make: 'Toyota', + model: 'Corolla', + year: 2002, +} + let car: { make: string model: string year: number -} +} = myCar -/* -//? A function that prints info about a car to stdout -// function printCar(car: { -// make: string -// model: string -// year: number -// }) { -// console.log(`${car.make} ${car.model} (${car.year})`) -// } +// ? A function that prints info about a car to stdout +function printCar(car: { + make: string + model: string + year: number + chargeVoltage?: number +}) { + let str = `${car.make} ${car.model} (${car.year})` + if (typeof car.chargeVoltage !== 'undefined') + str += `// ${car.chargeVoltage}v` + console.log(str) +} -// printCar(car) +printCar(car) -/* //* Optional properties //? Insert into function printCar // let str = `${car.make} ${car.model} (${car.year})` // car.chargeVoltage -// if (typeof car.chargeVoltage !== "undefined") -// str += `// ${car.chargeVoltage}v` - -/* -// printCar({ //? original fn works -// make: "Honda", -// model: "Accord", -// year: 2017, -// }) - -// printCar({ //? optional property works too! -// make: "Tesla", -// model: "Model 3", -// year: 2020, -// chargeVoltage: 220, -// }) - -/* + +printCar({ + //? original fn works + make: 'Honda', + model: 'Accord', + year: 2017, +}) + +printCar({ + //? optional property works too! + make: 'Tesla', + model: 'Model 3', + year: 2020, + chargeVoltage: 220, +}) + //* Excess property checking -// printCar({ -// make: "Tesla", -// model: "Model 3", -// year: 2020, -// color: "RED", //? EXTRA PROPERTY -// }) +printCar({ + ...{ + //? original fn works if we use spread syntax + make: 'Tesla', + model: 'Model 3', + year: 2020, + color: 'RED', //? EXTRA PROPERTY + }, +}) + +// above code can also be written as: +printCar({ + ...{ + make: 'Tesla', + model: 'Model 3', + year: 2020, + }, + ...{ color: 'RED' }, +}) -/* //* Index signatures //? Dictionary of phone #s -// const phones = { -// home: { country: "+1", area: "211", number: "652-4515" }, -// work: { country: "+1", area: "670", number: "752-5856" }, -// fax: { country: "+1", area: "322", number: "525-4357" }, -// } -/* -//? Model as an index signature -// const phones: { -// [k: string]: { -// country: string -// area: string -// number: string -// } -// } = {} +const phones: { + mobile: { + country: string + area: string + number: string + } + [k: string]: { + country: string + area: string + number: string + } +} = { + home: { country: '+1', area: '211', number: '652-4515' }, + work: { country: '+1', area: '670', number: '752-5856' }, + mobile: { country: '+1', area: '322', number: '525-4357' }, +} + +console.log(phones.home.country) +console.log(phones.work) +console.log(phones.fax) //! undefined, but no error! +console.log(phones.mobile) //! still works, even though we have an index signature! //* Array Types -/* -// const fileExtensions = ["js", "ts"] +const fileExtensions = ['js', 'ts'] // ^? string[] -// const cars = [ //? Let's look at an array of objects -// { -// make: "Toyota", -// model: "Corolla", -// year: 2002, -// }, -// ] - +const cars = [ + //? Let's look at an array of objects + { + make: 'Toyota', + model: 'Corolla', + year: 2002, + }, +] //* Tuples -/* -// let myCar = [ -// 2002, // Year -// "Toyota", // Make -// "Corolla" // Model -// ] -// const [year, make, model] = myCar //✔️ Destructuring + +let myCar2 = [ + 2002, // Year + 'Toyota', // Make + 'Corolla', // Model +] +const [year, make, model] = myCar2 //✔️ Destructuring //? Inference doesn't work very well for tuples -/* -// myCar = ["Honda", 2017, "Accord", "Sedan"] //! Wrong convention -/* -// let myCar: [number, string, string] = [ -// 2002, -// "Toyota", -// "Corolla", -// ] -// myCar = ["Honda", 2017, "Accord"] //! Wrong convention -// myCar = [2017, "Honda", "Accord", "Sedan"] //! Too many elements +myCar2 = ['Honda', 2017, 'Accord', 'Sedan'] //! Wrong convention + +let myCar3: [number, string, string] = [2002, 'Toyota', 'Corolla'] +myCar3 = ['Honda', 2017, 'Accord'] //! Wrong convention +myCar3 = [2017, 'Honda', 'Accord', 'Sedan'] //! Too many elements + +let studInfo: [string, number, boolean] = ['Alice', 12345, true] +const [name, id, isEnrolled] = studInfo + +let studArr = ['Alice', 'Ankit', 'Anand'] //? Inference: string[] +const [stud1, stud2, stud3, stud4] = studArr //! stud4 is undefined, but no error! //* `readonly` tuples -/* -// const numPair: [number, number] = [4, 5]; //✔️ Valid -// const numTriplet: [number, number, number] = [7]; //! Invalid -// [101, 102, 103].length //? number[].length -// numPair.length //? [number, number] length +const numPair: [number, number] = [4, 5] //✔️ Valid +const numTriplet: [number, number, number] = [7][(101, 102, 103)] //! Invalid + .length //? number[].length +numPair.length //? [number, number] length -// numPair.push(6) // [4, 5, 6] -// numPair.pop() // [4, 5] -// numPair.pop() // [4] -// numPair.pop() // [] +numPair.push(6) // [4, 5, 6] +numPair.pop() // [4, 5] +numPair.pop() // [4] +numPair.pop() // [] -// numPair.length //! ❌ DANGER ❌ +numPair.length //! ❌ DANGER ❌ -// const roNumPair: readonly [number, number] = [4, 5] -// roNumPair.length -// roNumPair.push(6) // [4, 5, 6] //! Not allowed -// roNumPair.pop() // [4, 5] //! Not allowed +const roNumPair: readonly [number, number] = [4, 5] +roNumPair.length +roNumPair.push(6) // [4, 5, 6] //! Not allowed +roNumPair.pop() // [4, 5] //! Not allowed /**/ diff --git a/packages/notes-ts-fundamentals-v4/src/05-structural-vs-nominal-types.ts b/packages/notes-ts-fundamentals-v4/src/05-structural-vs-nominal-types.ts index 3157beec3..79791ae39 100644 --- a/packages/notes-ts-fundamentals-v4/src/05-structural-vs-nominal-types.ts +++ b/packages/notes-ts-fundamentals-v4/src/05-structural-vs-nominal-types.ts @@ -5,6 +5,18 @@ class Car { model: string year: number isElectric: boolean + + constructor( + make: string, + model: string, + year: number, + isElectric: boolean, + ) { + this.make = make + this.model = model + this.year = year + this.isElectric = isElectric + } } class Truck { @@ -12,8 +24,23 @@ class Truck { model: string year: number towingCapacity: number + + constructor( + make: string, + model: string, + year: number, + towingCapacity: number, + ) { + this.make = make + this.model = model + this.year = year + this.towingCapacity = towingCapacity + } } +const newCar = new Car('Toyota', 'Camry', 2020, false) +const newTruck = new Truck('Ford', 'F-150', 2020, 13000) + const vehicle = { make: 'Honda', model: 'Accord', @@ -27,10 +54,17 @@ function printCar(car: { }) { console.log(`${car.make} ${car.model} (${car.year})`) } -/* -//printCar(new Car()) //✔️ Fine -//printCar(new Truck()) //✔️ Fine -//printCar(vehicle) //✔️ Fine + +printCar(newCar) //✔️ Fine +printCar(newTruck) //✔️ Fine +printCar(vehicle) //✔️ Fine /**/ +function sum(a: number, b: number) { + return a + b +} + +const add = sum +add(2, 3) //✔️ Fine + export default {} diff --git a/packages/notes-ts-fundamentals-v4/src/06-union-and-intersection-types.ts b/packages/notes-ts-fundamentals-v4/src/06-union-and-intersection-types.ts index cd915e77c..6882e1bc4 100644 --- a/packages/notes-ts-fundamentals-v4/src/06-union-and-intersection-types.ts +++ b/packages/notes-ts-fundamentals-v4/src/06-union-and-intersection-types.ts @@ -1,109 +1,108 @@ //* Union types in TypeScript const humidity = 79 //? Recall literal types +// humidity = 80 //? this is not a valid assignment, because 80 is not the literal type 79 //? Create types for two sets of numbers //? A set of numbers from 1 to 5 type OneThroughFive = 1 | 2 | 3 | 4 | 5 let lowNumber: OneThroughFive = 3 //✔️ Valid -// lowNumber = 8 //! 8 is not in the set +lowNumber = 8 //! 8 is not in the set //? A set of even numbers from 1 to 9 type Evens = 2 | 4 | 6 | 8 let evenNumber: Evens = 2 //✔️ Valid -// evenNumber = 5; //! 5 is not in the set +evenNumber = 5 //! 5 is not in the set -/* // //? A set of numbers from 1 to 5 OR a set of even numbers from 1 to 9 -// let evenOrLowNumber = 5 as Evens | OneThroughFive; +let evenOrLowNumber = 5 as Evens | OneThroughFive -/* // //? Control flow sometimes results in union types -// function flipCoin() { -// if (Math.random() > 0.5) return "heads" -// return "tails" -// } +function flipCoin(): 'heads' | 'tails' { + if (Math.random() > 0.5) return 'heads' + return 'tails' +} -// const outcome = flipCoin() +const outcome = flipCoin() // // ^? "heads" | "tails" // //? A more complicated example -// const success = ["success", { name: "Mike North", email: "mike@example.com" }] as const -// const fail = ["error", new Error("Something went wrong!")] as const +const success = [ + 'success', + { name: 'Mike North', email: 'mike@example.com' }, +] as const +const fail = ['error', new Error('Something went wrong!')] as const -/* -// function maybeGetUserInfo() { -// if (flipCoin() === "heads") { -// return success -// } else { -// return fail -// } -// } +function maybeGetUserInfo() { + if (flipCoin() === 'heads') { + return success + } else { + return fail + } +} -// const outcome2 = maybeGetUserInfo() +const outcome2 = maybeGetUserInfo() //* Working with union types -/* + //? Think critically: "AND" vs "OR", as it pertains to the contents of the set, //? vs the assumptions we can make about the value -// function printEven(even: Evens): void { } -// function printLowNumber(lowNum: OneThroughFive): void { } -// function printEvenNumberUnder5(num: 2 | 4): void { } -// function printNumber(num: number): void { } +function printEven(even: Evens): void {} +function printLowNumber(lowNum: OneThroughFive): void {} +function printEvenNumberUnder5(num: 2 | 4): void {} +function printNumber(num: number): void {} -// let x = 5 as Evens | OneThroughFive; +let x = 5 as Evens | OneThroughFive -/* //? What does Evens | OneThroughFive accept as values? // let evenOrLowNumber: Evens | OneThroughFive; -// evenOrLowNumber = 6 //✔️ An even -// evenOrLowNumber = 3 //✔️ A low number -// evenOrLowNumber = 4 //✔️ A even low number +evenOrLowNumber = 6 //✔️ An even +evenOrLowNumber = 3 //✔️ A low number +evenOrLowNumber = 4 //✔️ A even low number //? What requirements can `Evens | OneThroughFive` meet? -// printEven(x) //! Not guaranteed to be even -// printLowNumber(x) //! Not guaranteed to be in {1, 2, 3, 4, 5} -// printEvenNumberUnder5(x) //! Not guaranteed to be in {2, 4} -// printNumber(x) //✔️ Guaranteed to be a number +printEven(x) //! Not guaranteed to be even +printLowNumber(x) //! Not guaranteed to be in {1, 2, 3, 4, 5} +printEvenNumberUnder5(x) //! Not guaranteed to be in {2, 4} +printNumber(x) //✔️ Guaranteed to be a number //* Narrowing with type guards -/* -// const [first, second] = outcome2 -// if (second instanceof Error) { -// // In this branch of your code, second is an Error -// second -// } else { -// // In this branch of your code, second is the user info -// second -// } + +const [first, second] = outcome2 +if (second instanceof Error) { + // In this branch of your code, second is an Error + second +} else { + // In this branch of your code, second is the user info + second +} //* Discriminated unions -/* -// if (first === "error") { -// // In this branch of your code, second is an Error -// second -// } else { -// // In this branch of your code, second is the user info -// second -// } + +if (first === 'error') { + // In this branch of your code, second is an Error + second +} else { + // In this branch of your code, second is the user info + second +} //* Intersection Types -/* -// //? What does Evens & OneThroughFive accept as values? -// let evenAndLowNumber: Evens & OneThroughFive; -// evenAndLowNumber = 6 //! Not in OneThroughFive -// evenAndLowNumber = 3 //! Not in Evens -// evenAndLowNumber = 4 //✔️ In both sets +// //? What does Evens & OneThroughFive accept as values? +let evenAndLowNumber: Evens & OneThroughFive +evenAndLowNumber = 6 //! Not in OneThroughFive +evenAndLowNumber = 3 //! Not in Evens +evenAndLowNumber = 4 //✔️ In both sets //? What requirements can `Evens & OneThroughFive` meet? -// let y = 4 as Evens & OneThroughFive; +let y = 4 as Evens & OneThroughFive -// printEven(y) //✔️ Guaranteed to be even -// printLowNumber(y) //✔️ Guaranteed to be in {1, 2, 3, 4, 5} -// printEvenNumberUnder5(y) //✔️ Guaranteed to be in {2, 4} -// printNumber(y) //✔️ Guaranteed to be a number +printEven(y) //✔️ Guaranteed to be even +printLowNumber(y) //✔️ Guaranteed to be in {1, 2, 3, 4, 5} +printEvenNumberUnder5(y) //✔️ Guaranteed to be in {2, 4} +printNumber(y) //✔️ Guaranteed to be a number /**/ diff --git a/packages/notes-ts-fundamentals-v4/src/07-interfaces-and-type-aliases.ts b/packages/notes-ts-fundamentals-v4/src/07-interfaces-and-type-aliases.ts index dd5c66f7d..825eb6ed4 100644 --- a/packages/notes-ts-fundamentals-v4/src/07-interfaces-and-type-aliases.ts +++ b/packages/notes-ts-fundamentals-v4/src/07-interfaces-and-type-aliases.ts @@ -3,207 +3,217 @@ type Amount = { currency: string value: number } -/* -// function printAmount(amt: Amount) { -// console.log(amt) -// const { currency, value } = amt -// console.log(`${currency} ${value}`) -// } +function printAmount(amt: Amount) { + console.log(amt) -// const donation = { -// currency: "USD", -// value: 30.0, -// description: "Donation to food bank", -// } + const { currency, value } = amt + console.log(`${currency} ${value}`) +} -// printAmount(donation) //✔️ Valid +const donation = { + currency: 'USD', + value: 30.0, + description: 'Donation to food bank', +} +printAmount(donation) //✔️ Valid //? Let's look at a familiar example from the last chapter -/* -// function flipCoin() { -// if (Math.random() > 0.5) return "heads" -// return "tails" -// } -// const success = ["success", { name: "Mike North", email: "mike@example.com" }] as const -// const fail = ["error", new Error("Something went wrong!")] as const - -// export function maybeGetUserInfo(): -// | readonly ["error", Error] -// | readonly ["success", { name: string; email: string }] { -// // implementation is the same in both examples -// if (flipCoin() === 'heads') { -// return success -// } else { -// return fail -// } -// } + +function flipCoin() { + if (Math.random() > 0.5) return 'heads' + return 'tails' +} +const success = [ + 'success', + { name: 'Mike North', email: 'mike@example.com' }, +] as const +const fail = ['error', new Error('Something went wrong!')] as const + +export function maybeGetUserInfo(): + | readonly ['error', Error] + | readonly ['success', { name: string; email: string }] { + // implementation is the same in both examples + if (flipCoin() === 'heads') { + return success + } else { + return fail + } +} //? Let's model the return type as an interface -/* -// type UserInfoOutcomeError = readonly ["error", Error] -// type UserInfoOutcomeSuccess = readonly [ -// "success", -// { readonly name: string; readonly email: string }, -// ] -// type UserInfoOutcome = -// | UserInfoOutcomeError -// | UserInfoOutcomeSuccess +type UserInfoOutcomeError = readonly ['error', Error] +type UserInfoOutcomeSuccess = readonly [ + 'success', + { readonly name: string; readonly email: string }, +] +type UserInfoOutcome = UserInfoOutcomeError | UserInfoOutcomeSuccess //* Inheritance in type aliases -/* -// type SpecialDate = Date & { getDescription(): string } -// const newYearsEve: SpecialDate -// // ^? -// = Object.assign( -// new Date(), -// { getDescription: () => "Last day of the year" } -// ) +type SpecialDate = Date & { getDescription(): string } + +const newYearsEve: SpecialDate = + // ^? + Object.assign(new Date(), { + getDescription: () => 'Last day of the year', + }) -// newYearsEve.getDescription -// // ^? +newYearsEve.getDescription +// ^? //* Interfaces -/* -// interface Amount2 { -// currency: string -// value: number -// } -// function printAmount2(amt: Amount2) { -// amt -// } +interface Amount2 { + currency: string + value: number +} + +function printAmount2(amt: Amount2) { + amt +} //* Inheritance in interfaces -/* + // //? `extends` keyword -// function consumeFood(arg) { } - -// class AnimalThatEats { -// eat(food) { -// consumeFood(food) -// } -// } -// class Cat extends AnimalThatEats { -// meow() { -// return "meow" -// } -// } - -// const c = new Cat() -// c.eat -// c.meow() - -/* -// interface Animal { -// isAlive(): boolean -// } -// interface Mammal extends Animal { -// getFurOrHairColor(): string -// } -// interface Hamster extends Mammal { -// squeak(): string -// } -// function careForHamster(h: Hamster) { -// h.getFurOrHairColor() -// h.squeak() -// // ^| -// } +function consumeFood(arg: string) {} + +class AnimalThatEats { + eat(food: string) { + consumeFood(food) + } +} +class Cat extends AnimalThatEats { + meow() { + return 'meow' + } +} +const c = new Cat() +c.eat('cat food') +c.meow() + +interface Animal { + isAlive(): boolean +} +interface Mammal extends Animal { + getFurOrHairColor(): string +} +interface Hamster extends Mammal { + squeak(): string +} +function careForHamster(h: Hamster) { + h.getFurOrHairColor() + h.squeak() + // ^| +} //? `implements` keyword -/* -// interface AnimalLike { -// eat(food): void -// } - -// class Dog implements AnimalLike { -// bark() { -// return "woof" -// } -// } -/* -// class LivingOrganism { //? A base class -// isAlive() { -// return true -// } -// } -// interface CanBark { //? Another interface -// bark(): string -// } -// class Dog2 -// extends LivingOrganism -// implements AnimalLike, CanBark { -// bark() { -// return "woof" -// } -// eat(food) { -// consumeFood(food) -// } -// } + +interface AnimalLike { + eat(food: string): void +} + +class Dog implements AnimalLike { + eat(food: string): void { + consumeFood(food) + } + bark() { + return 'woof' + } +} + +const d = new Dog() +d.eat('dog food') +d.bark() + +class LivingOrganism { + //? A base class + isAlive() { + return true + } +} + +interface CanBark { + //? Another interface + bark(): string +} + +class Dog2 + extends LivingOrganism + implements Animal, AnimalLike, CanBark +{ + bark() { + return 'woof' + } + eat(food: string) { + consumeFood(food) + } +} //? Implements sometimes works with type aliases -/* -// type CanJump = { -// jumpToHeight(): number -// // | [number, number] -// } -// class Dog3 implements CanJump { -// jumpToHeight() { -// return 1.7 -// } -// eat(food) { -// consumeFood(food) -// } -// } - -// type CanBark = -// | number -// | { -// bark(): string -// } + +type CanJump = { + jumpToHeight(): number | [number, number] +} + +type CanBark2 = + | number + | { + bark(): string + } + +class Dog3 + implements + CanJump, // This works because CanJump is an object type + CanBark2 +{ + // This won't work because CanBark2 is a union type, not an object type + jumpToHeight() { + return 1.7 + } + eat(food: string) { + consumeFood(food) + } +} //* Open interfaces -/* -// function feed(animal: AnimalLike) { -// animal.eat -// animal.isAlive -// } -/* -// interface AnimalLike { //✔️ Additional declaration is OK -// isAlive(): boolean -// } +function feed(animal: AnimalLike) { + if (animal.isAlive()) animal.eat('food') +} + +interface AnimalLike { + //✔️ Additional declaration is OK + isAlive(): boolean +} //* Use case: augmenting existing types -/* -// window.document // an existing property -// // ^? (property) document: Document -// window.exampleProperty = 42 -// // ^? (property) exampleProperty: number +window.document // an existing property +// ^? (property) document: Document +window.exampleProperty = 42 +// ^? (property) exampleProperty: number -/* -//// tells TS that `exampleProperty` exists -// declare global { -// interface Window { -// exampleProperty: number -// } -// } +// tells TS that `exampleProperty` exists +declare global { + interface Window { + exampleProperty: number + } +} //* Recursive types -/* -// type NestedNumbers = number | NestedNumbers[] - -// const val: NestedNumbers = [3, 4, [5, 6, [7], 59], 221] -/* -// if (typeof val !== "number") { -// val.push(41) -// val.push("this will not work") //! No strings allowed -// } + +type NestedNumbers = number | NestedNumbers[] + +const val: NestedNumbers = [3, 4, [5, 6, [7], 59], 221] + +if (typeof val !== 'number') { + val.push(41) + val.push('this will not work') //! No strings allowed +} /**/ export default {} diff --git a/packages/notes-ts-fundamentals-v4/src/08-json-types.ts b/packages/notes-ts-fundamentals-v4/src/08-json-types.ts index c6b573b93..9f8de9a7b 100644 --- a/packages/notes-ts-fundamentals-v4/src/08-json-types.ts +++ b/packages/notes-ts-fundamentals-v4/src/08-json-types.ts @@ -10,18 +10,19 @@ or one of the following three literal names: · true · null */ +type JSONPrimitive = string | number | boolean | null /** * A JSON object type { } */ -type JSONObject = any +type JSONObject = { [key: string]: JSONValue } /** * A JSON array type [ ] */ -type JSONArray = any +type JSONArray = JSONValue[] /** * A type representing any valid JSON value */ -type JSONValue = any +type JSONValue = JSONPrimitive | JSONObject | JSONArray //! DO NOT EDIT ANY CODE BELOW THIS LINE function isJSON(arg: JSONValue) {} @@ -36,11 +37,11 @@ isJSON(null) //✔️ null values isJSON({ a: { b: [2, 3, 'foo', null, false] } }) //✔️ A complex object //! NEGATIVE test cases (must fail) -//// @ts-expect-error +// @ts-expect-error isJSON(() => '') //! Functions are not valid JSON -//// @ts-expect-error +// @ts-expect-error isJSON(class {}) //! Classes are not valid JSON -//// @ts-expect-error +// @ts-expect-error isJSON(undefined) //! undefined is not valid JSON -//// @ts-expect-error +// @ts-expect-error isJSON(BigInt(143)) //! BigInts are not valid JSON diff --git a/packages/notes-ts-fundamentals-v4/src/09-type-queries.ts b/packages/notes-ts-fundamentals-v4/src/09-type-queries.ts index cc18d6f5c..26234f6f0 100644 --- a/packages/notes-ts-fundamentals-v4/src/09-type-queries.ts +++ b/packages/notes-ts-fundamentals-v4/src/09-type-queries.ts @@ -1,44 +1,45 @@ //* keyof type DatePropertyNames = keyof Date -/* -// type DateStringPropertyNames = DatePropertyNames & string -// type DateSymbolPropertyNames = DatePropertyNames & symbol + +type DateStringPropertyNames = DatePropertyNames & string +type DateSymbolPropertyNames = DatePropertyNames & symbol //* typeof -/* -// async function main() { -// const apiResponse = await Promise.all([ -// fetch("https://example.com"), -// Promise.resolve("Titanium White"), -// ]) -// type ApiResponseType = typeof apiResponse -// } -/* -//?^ note: type alias within a function scope! -// const MyAjaxConstructor = CSSRule -// CSSRule.STYLE_RULE -// const myAjax = new CSSRule() +async function main() { + const apiResponse = await Promise.all([ + fetch('https://example.com'), + Promise.resolve('Titanium White'), + ]) + type ApiResponseType = typeof apiResponse +} + +//?^ note: type alias within a function scope! +const MyAjaxConstructor = CSSRule +CSSRule.STYLE_RULE +const myAjax = new CSSRule() //* Indexed Access Types -/* -// interface Car { -// make: string -// model: string -// year: number -// color: { -// red: string -// green: string -// blue: string -// } -// } - -// let carColor: Car["color"] //✔️ Reaching for something that exists -// let carSomething: Car["not-something-on-car"] //! Reaching for something invalid -// let carColorRedComponent: Car["color"]["red"] //✔️ Reaching for something nested -// let carProperty: Car["color" | "year"] // ✔️ Passing a union type through the index +interface Car { + make: string + model: string + year: number + color: { + red: string + green: string + blue: string + } +} + +let carColor: Car['color'] //✔️ Reaching for something that exists +let carSomething: Car['not-something-on-car'] //! Reaching for something invalid +let carColorRedComponent: Car['color']['red'] //✔️ Reaching for something nested +let carProperty: Car['color' | 'year'] // ✔️ Passing a union type through the index + +// keyof: Object.keys() for types +// typeof: typeof operator for types //* Use case: the type registry pattern /* diff --git a/packages/notes-ts-fundamentals-v4/src/10-callables-and-constructables.ts b/packages/notes-ts-fundamentals-v4/src/10-callables-and-constructables.ts index 7d1ab7b66..44778f07f 100644 --- a/packages/notes-ts-fundamentals-v4/src/10-callables-and-constructables.ts +++ b/packages/notes-ts-fundamentals-v4/src/10-callables-and-constructables.ts @@ -10,99 +10,96 @@ const add: TwoNumberCalculation = (a, b) => a + b const subtract: TwoNumberCalc = (x, y) => x - y //* `void` -/* -// function printFormattedJSON(obj: string[]) { -// console.log(JSON.stringify(obj, null, " ")) -// } -// const x = printFormattedJSON(["hello", "world"]) +function printFormattedJSON(obj: string[]) { + console.log(JSON.stringify(obj, null, ' ')) +} -/* -// function invokeInFourSeconds(callback: () => undefined) { -// setTimeout(callback, 4000) -// } -// function invokeInFiveSeconds(callback: () => void) { -// setTimeout(callback, 5000) -// } +const x = printFormattedJSON(['hello', 'world']) -// const values: number[] = [] -// invokeInFourSeconds(() => values.push(4)) //! Error: Type 'undefined' is not assignable to type 'number'. -// invokeInFiveSeconds(() => values.push(4)) +function invokeInFourSeconds(callback: () => undefined) { + setTimeout(callback, 4000) +} +function invokeInFiveSeconds(callback: () => void) { + setTimeout(callback, 5000) +} + +const values: number[] = [] +invokeInFourSeconds(() => values.push(4)) //! Error: Type 'undefined' is not assignable to type 'number'. +invokeInFiveSeconds(() => values.push(4)) //* Constructables -/* -// interface DateConstructor { -// new(value: number): Date -// } -// let MyDateConstructor: DateConstructor = Date -// const d = new MyDateConstructor(1697923072611) +interface DateConstructor { + new (value: number): Date +} + +let MyDateConstructor: DateConstructor = Date +const d = new MyDateConstructor(1697923072611) //* Function overloads -/* -// type FormSubmitHandler = (data: FormData) => void -// type MessageHandler = (evt: MessageEvent) => void - -// function handleMainEvent( -// elem: HTMLFormElement | HTMLIFrameElement, -// handler: FormSubmitHandler | MessageHandler -// ) { } - -// const myFrame = document.getElementsByTagName("iframe")[0] -// handleMainEvent(myFrame, (val) => { -// }) - -/* -// //? Add above handleMainEvent function declaration -// function handleMainEvent( -// elem: HTMLFormElement, -// handler: FormSubmitHandler -// ) -// function handleMainEvent( -// elem: HTMLIFrameElement, -// handler: MessageHandler -// ) + +type FormSubmitHandler = (data: FormData) => void +type MessageHandler = (evt: MessageEvent) => void + +function handleMainEvent( + elem: HTMLFormElement, + handler: FormSubmitHandler, +): void +function handleMainEvent( + elem: HTMLIFrameElement, + handler: MessageHandler, +): void + +function handleMainEvent( + elem: HTMLFormElement | HTMLIFrameElement, + handler: FormSubmitHandler | MessageHandler, +) { + console.log('the real function') +} + +const myFrame = document.getElementsByTagName('iframe')[0] +handleMainEvent(myFrame, (val) => {}) + // //? Form handler has a specific type now! -// const myForm = document.getElementsByTagName("form")[0] -// handleMainEvent(myForm, (val) => { -// }) +const myForm = document.getElementsByTagName('form')[0] +handleMainEvent(myForm, (val) => {}) //* `this` types -/* -// function myClickHandler(event: Event) { -// // this.disabled = true -// } -// myClickHandler(new Event("click")) // maybe ok? - -/* -// const myButton = document.getElementsByTagName("button")[0] -// const boundHandler = myClickHandler.bind(myButton) -// boundHandler(new Event("click")) // bound version: ok -// myClickHandler.call(myButton, new Event("click")) // also ok + +function myClickHandler(this: HTMLButtonElement, event: Event) { + this.disabled = true +} +myClickHandler(new Event('click')) // maybe ok? + +const myButton = document.getElementsByTagName('button')[0] +const boundHandler = myClickHandler.bind(myButton) +boundHandler(new Event('click')) // bound version: ok +myClickHandler.call(myButton, new Event('click')) // also ok //* Function best practices -/* + //? Explicit function return types -// type JSONPrimitive = string | number | boolean | null -// type JSONObject = { [k: string]: JSONValue } -// type JSONArray = JSONValue[] -// type JSONValue = JSONArray | JSONObject | JSONPrimitive - -// export async function getData(url: string) { -// const resp = await fetch(url) -// // if (resp.ok) { -// const data = (await resp.json()) as { -// properties: string[] -// } -// return data -// // } -// } - -// function loadData() { -// getData("https://example.com").then((result) => { -// console.log(result.properties.join(", ")) -// // ^? -// }) -// } +type JSONPrimitive = string | number | boolean | null +type JSONObject = { [k: string]: JSONValue } +type JSONArray = JSONValue[] +type JSONValue = JSONArray | JSONObject | JSONPrimitive + +export async function getData(url: string) { + const resp = await fetch(url) + // if (resp.ok) { + const data = (await resp.json()) as { + properties: string[] + } + return data + // } +} + +function loadData() { + getData('https://example.com').then((result) => { + console.log(result.properties.join(', ')) + // ^? + }) +} /**/ export default {} diff --git a/packages/notes-ts-fundamentals-v4/src/11-classes.ts b/packages/notes-ts-fundamentals-v4/src/11-classes.ts index 9e47bf816..706d1209c 100644 --- a/packages/notes-ts-fundamentals-v4/src/11-classes.ts +++ b/packages/notes-ts-fundamentals-v4/src/11-classes.ts @@ -2,133 +2,117 @@ //? Field types class Car { - make: string - model: string - year: number - constructor(make: string, model: string, year: number) { - this.make = make - this.model = model - // ^? - this.year = year + static #nextSerialNumber: number + static #generateSerialNumber() { + return this.#nextSerialNumber++ + } + static { + // `this` is the static scope + fetch('https://api.example.com/vin_number_data') + .then((response) => response.json()) + .then((data) => { + this.#nextSerialNumber = data.mostRecentInvoiceId + 1 + }) + } + + // serialNumber = Car.generateSerialNumber() + readonly #serialNumber = Car.#generateSerialNumber() + protected get serialNumber(): number { + return this.#serialNumber + } + + constructor( + public make: string, + public model: string, + public year: number, + ) {} + + honk(duration: number): string { + return `h${'o'.repeat(duration)}nk` + } + + getLabel() { + return `${this.make} ${this.model} ${this.year} - #${this.serialNumber}` + } + + equals(other: any) { + if ( + other && + typeof other === 'object' && + #serialNumber in other + ) { + other + // ^? + return other.#serialNumber === this.#serialNumber + } + return false } } let sedan = new Car('Honda', 'Accord', 2017) -// sedan.activateTurnSignal("left") //! not safe! -// new Car(2017, "Honda", "Accord") //! not safe! +sedan.activateTurnSignal('left') //! not safe! +new Car(2017, 'Honda', 'Accord') //! not safe! -/* //? method types -// honk(duration: number): string { -// return `h${'o'.repeat(duration)}nk`; -// } -// const c = new Car("Honda", "Accord", 2017); -// c.honk(5); // "hooooonk" - -/* -//? static member fields -// static nextSerialNumber = 100 -// static generateSerialNumber() { return this.nextSerialNumber++ } -// getLabel() { -// return `${this.make} ${this.model} ${this.year} - #${this.serialNumber}` -// } -// console.log( new Car("Honda", "Accord", 2017)) -// // > "Honda Accord 2017 - #100 -// console.log( new Car("Toyota", "Camry", 2022)) -// // > "Toyota Camry 2022 - #101 - -/* -//? static blocks -// static { -// // `this` is the static scope -// fetch("https://api.example.com/vin_number_data") -// .then(response => response.json()) -// .then(data => { -// this.nextSerialNumber = data.mostRecentInvoiceId + 1; -// }) -// } -// serialNumber = Car.generateSerialNumber() - -//* Access modifier keywords -/* -//? on member fields -// private _serialNumber = Car.generateSerialNumber() -// protected get serialNumber() { -// return this._serialNumber -// } -// const s = new Sedan("Nissan", "Altima", 2020) -// s.serialNumber - -/* -//? on static fields -// private static nextSerialNumber: number -// private static generateSerialNumber() { return this.nextSerialNumber++ } -// Car.generateSerialNumber() - -//* JS private #fields -/* -//? member fields -// #serialNumber = Car.generateSerialNumber() -// c.#serialNumber - -/* -//? static fields -// static #nextSerialNumber: number -// static #generateSerialNumber() { return this.#nextSerialNumber++ } -// #serialNumber = Car.#generateSerialNumber() +const c = new Car('Honda', 'Accord', 2017) +c.honk(5) // "hooooonk" + +console.log(new Car('Honda', 'Accord', 2017)) +// > "Honda Accord 2017 - #100 +console.log(new Car('Toyota', 'Camry', 2022)) +// > "Toyota Camry 2022 - #101 + +serialNumber = Car.generateSerialNumber() //! not safe! because it's static, not instance-specific + +const s = new Car('Nissan', 'Altima', 2020) +// > "Nissan Altima 2020 - #102" + +Car.generateSerialNumber() //! not safe! because it's static, not instance-specific + +c.#serialNumber //! not safe! because it's private //* Private field presence checks -/* -// equals(other: unknown) { -// if (other && -// typeof other === 'object' && -// #serialNumber in other) { -// other -// // ^? -// return other.#serialNumber = this.#serialNumber -// } -// return false -// } -// const c2 = c1 -// c2.equals(c1) + +const c2 = c1 +c2.equals(c1) //* readonly -/* + // readonly #serialNumber = Car.#generateSerialNumber() // changeSerialNumber(num: number) { // this.#serialNumber = num // } //* Parameter properties -/* + // constructor( // public make: string, // public model: string, // public year: number // ) {} -// class Base {} +class Base {} -// class Car2 extends Base { -// foo = console.log("class field initializer") -// constructor(public make: string) { -// super() -// console.log("custom constructor stuff") -// } -// } +class Car2 extends Base { + foo = console.log('class field initializer') + constructor(public make: string) { + super() + console.log('custom constructor stuff') + } +} //* Overrides -/* -// class Truck extends Car { -// hoonk() { // OOPS! -// console.log("BEEP") -// } -// } +class Truck extends Car { + override honk() { + // OOPS! + return 'beep' + } +} -// const t = new Truck("Ford", "F-150", 2020); -// t.honk(); // "beep" +const t = new Truck('Ford', 'F-150', 2020) +t.honk() // "beep" //? override keyword // override hoonk() { // OOPS! diff --git a/packages/notes-ts-fundamentals-v4/src/12-type-guards.ts b/packages/notes-ts-fundamentals-v4/src/12-type-guards.ts index 12ad61d77..957d1d631 100644 --- a/packages/notes-ts-fundamentals-v4/src/12-type-guards.ts +++ b/packages/notes-ts-fundamentals-v4/src/12-type-guards.ts @@ -41,140 +41,143 @@ else if ('dateRange' in value) { // ^? } //* User-defined type guards -/* -// interface CarLike { -// make: string -// model: string -// year: number -// } - -// let maybeCar: any - -// // the guard -// if ( -// maybeCar && -// typeof maybeCar === "object" && -// "make" in maybeCar && -// typeof maybeCar["make"] === "string" && -// "model" in maybeCar && -// typeof maybeCar["model"] === "string" && -// "year" in maybeCar && -// typeof maybeCar["year"] === "number" -// ) { -// maybeCar -// // ^? -// } -/* -// // the guard -// function isCarLike(valueToTest: any) { -// return ( -// valueToTest && -// typeof valueToTest === "object" && -// "make" in valueToTest && -// typeof valueToTest["make"] === "string" && -// "model" in valueToTest && -// typeof valueToTest["model"] === "string" && -// "year" in valueToTest && -// typeof valueToTest["year"] === "number" -// ) -// } - -// // using the guard -// if (isCarLike(maybeCar)) { -// maybeCar -// // ^? -// } + +interface CarLike { + make: string + model: string + year: number +} + +let maybeCar: any + +// the guard +if ( + maybeCar && + typeof maybeCar === 'object' && + 'make' in maybeCar && + typeof maybeCar['make'] === 'string' && + 'model' in maybeCar && + typeof maybeCar['model'] === 'string' && + 'year' in maybeCar && + typeof maybeCar['year'] === 'number' +) { + maybeCar + // ^? +} + +// the guard +function isCarLike(valueToTest: any): valueToTest is CarLike { + return ( + valueToTest && + typeof valueToTest === 'object' && + 'make' in valueToTest && + typeof valueToTest['make'] === 'string' && + 'model' in valueToTest && + typeof valueToTest['model'] === 'string' && + 'year' in valueToTest && + typeof valueToTest['year'] === 'number' + ) +} + +// using the guard +if (isCarLike(maybeCar)) { + maybeCar + // ^? +} //* value is foo -/* + // function isCarLike(valueToTest: any): valueToTest is CarLike { //* asserts value is foo -/* -// function assertsIsCarLike( -// valueToTest: any -// ): asserts valueToTest is CarLike { -// if ( -// !( -// valueToTest && -// typeof valueToTest === "object" && -// "make" in valueToTest && -// typeof valueToTest["make"] === "string" && -// "model" in valueToTest && -// typeof valueToTest["model"] === "string" && -// "year" in valueToTest && -// typeof valueToTest["year"] === "number" -// ) -// ) -// throw new Error( -// `Value does not appear to be a CarLike${valueToTest}` -// ) -// } -// assertsIsCarLike(maybeCar) -// maybeCar + +function assertsIsCarLike( + valueToTest: any, +): asserts valueToTest is CarLike { + if ( + !( + valueToTest && + typeof valueToTest === 'object' && + 'make' in valueToTest && + typeof valueToTest['make'] === 'string' && + 'model' in valueToTest && + typeof valueToTest['model'] === 'string' && + 'year' in valueToTest && + typeof valueToTest['year'] === 'number' + ) + ) + throw new Error( + `Value does not appear to be a CarLike${valueToTest}`, + ) +} +assertsIsCarLike(maybeCar) +maybeCar //* Use with private #field presence checks -/* -// class Car { -// static #nextSerialNumber: number = 100 -// static #generateSerialNumber() { return this.#nextSerialNumber++ } - -// #serialNumber = Car.#generateSerialNumber() - -// static isCar(other: any): other is Car { -// if (other && // is it truthy -// typeof other === "object" && // and an object -// #serialNumber in other) { // and we can find a private field that we can access from here -// // then it *must* be a car -// other -// // ^? -// return true -// } -// return false -// } -// } - -// let val: any - -// if (Car.isCar(val)) { -// val -// // ^? -// } +class Car { + static #nextSerialNumber: number = 100 + static #generateSerialNumber() { + return this.#nextSerialNumber++ + } + + #serialNumber = Car.#generateSerialNumber() + + static isCar(other: any): other is Car { + if ( + other && // is it truthy + typeof other === 'object' && // and an object + #serialNumber in other + ) { + // and we can find a private field that we can access from here + // then it *must* be a car + other + // ^? + return true + } + return false + } +} + +let val: any + +if (Car.isCar(val)) { + val + // ^? +} //* Narrowing with switch(true) -/* -// class Fish { -// swim(): void { } -// } -// class Bird { -// fly(): void { } -// } - - -// switch (true) { -// case val instanceof Bird: -// val.fly() -// break -// case val instanceof Fish: -// val.swim() -// break -// } + +class Fish { + swim(): void {} +} +class Bird { + fly(): void {} +} + +switch (true) { + case val instanceof Bird: + val.fly() + break + case val instanceof Fish: + val.swim() + break +} //* Writing high-quality type guards -/* -// //! EXAMPLE OF A BAD TYPE GUARD -// function isNull(val: any): val is null { -// return !val //! Lies! -// } -// const empty = "" -// const zero = 0 -// if (isNull(zero)) { -// console.log(zero) //? is it really impossible to get here? -// } -// if (isNull(empty)) { -// console.log(empty) //? is it really impossible to get here? -// } + +//! EXAMPLE OF A BAD TYPE GUARD +function isNull(val: any): val is null { + return !val //! Lies! +} +const empty = '' +const zero = 0 +if (isNull(zero)) { + console.log(zero) //? is it really impossible to get here? +} +if (isNull(empty)) { + console.log(empty) //? is it really impossible to get here? +} /**/ export default {} diff --git a/packages/notes-ts-fundamentals-v4/src/13-generics.ts b/packages/notes-ts-fundamentals-v4/src/13-generics.ts index d2e42c931..8dd01984d 100644 --- a/packages/notes-ts-fundamentals-v4/src/13-generics.ts +++ b/packages/notes-ts-fundamentals-v4/src/13-generics.ts @@ -20,33 +20,33 @@ const phoneDict = { }, /*... and so on */ } -/* -// interface PhoneInfo { -// customerId: string -// areaCode: string -// num: string -// } -// function listToDict( -// list: PhoneInfo[], // take the list as an argument -// idGen: (arg: PhoneInfo) => string, // a callback to get Ids -// ): { [k: string]: PhoneInfo } {} +interface PhoneInfo { + customerId: string + areaCode: string + num: string +} -/* -//? function body -// // create an empty dictionary -// const dict: { [k: string]: PhoneInfo } = {} +function listToDict( + list: T[], // take the list as an argument + idGen: (arg: T) => string, // a callback to get Ids +): { [k: string]: T } { + // create an empty dictionary + const dict: { [k: string]: T } = {} -// // Loop through the array -// list.forEach((element) => { -// const dictKey = idGen(element) -// dict[dictKey] = element // store element under key -// }) + // Loop through the array + list.forEach((element) => { + const dictKey = idGen(element) + dict[dictKey] = element // store element under key + }) + + return dict +} // // return the dictionary -// const result = listToDict(phoneList, (item) => item.customerId) -// console.log(result) -/* +const result = listToDict(phoneList, (item) => item.customerId) +console.log(result) + //? An attempt to generalize the above function to work with any type of list // function listToDict( @@ -56,7 +56,6 @@ const phoneDict = { //* Defining a type parameter -/* // function listToDict( // list: T[], // idGen: (arg: T) => string, @@ -65,16 +64,15 @@ const phoneDict = { // return dict // } -// function wrapInArray(arg: T): [T] { -// return [arg] -// } -// wrapInArray(3) +function wrapInArray(arg: T): [T] { + return [arg] +} +wrapInArray(3) // // ^? -// wrapInArray(new Date()) +wrapInArray(new Date()) // // ^? -// wrapInArray(new RegExp("/s/")) +wrapInArray(new RegExp('/s/')) -/* //? Let's try it! // listToDict( // [ @@ -89,10 +87,9 @@ const phoneDict = { // ) //* Best practices -/* -// function returnAs(arg: any): T { -// return arg //! an `any` that will _seem_ like a `T` -// } // may as well just cast +function returnAs(arg: T): T { + return arg //! an `any` that will _seem_ like a `T` +} // may as well just cast /**/ export default {} diff --git a/packages/notes-ts-fundamentals-v4/src/14-dict-map-filter-reduce.ts b/packages/notes-ts-fundamentals-v4/src/14-dict-map-filter-reduce.ts deleted file mode 100644 index 8b19d2d90..000000000 --- a/packages/notes-ts-fundamentals-v4/src/14-dict-map-filter-reduce.ts +++ /dev/null @@ -1,119 +0,0 @@ -// @ts-nocheck -//* Dictionaries: map, filter, reduce - -///////////////////////////////////////// -/////////// TESTING UTILITIES /////////// -//////// no need to modify these //////// -///////////////////////////////////////// -// @errors: 7006 7006 7006 7006 7006 -console.clear() - -function assertEquals(found: T, expected: T, message: string) { - if (found !== expected) - throw new Error( - `❌ Assertion failed: ${message}\nexpected: ${expected}\nfound: ${found}`, - ) - console.log(`✅ OK ${message}`) -} - -function assertOk(value: any, message: string) { - if (!value) throw new Error(`❌ Assertion failed: ${message}`) - console.log(`✅ OK ${message}`) -} -/// ---cut--- - -///// SAMPLE DATA FOR YOUR EXPERIMENTATION PLEASURE (do not modify) -const fruits = { - apple: { color: 'red', mass: 100 }, - grape: { color: 'red', mass: 5 }, - banana: { color: 'yellow', mass: 183 }, - lemon: { color: 'yellow', mass: 80 }, - pear: { color: 'green', mass: 178 }, - orange: { color: 'orange', mass: 262 }, - raspberry: { color: 'red', mass: 4 }, - cherry: { color: 'red', mass: 5 }, -} - -interface Dict { - [k: string]: T -} - -// Array.prototype.map, but for Dict -function mapDict(...args: any[]): any {} -// Array.prototype.filter, but for Dict -function filterDict(...args: any[]): any {} -// Array.prototype.reduce, but for Dict -function reduceDict(...args: any[]): any {} - -///////////////////////////////////////// -///////////// TEST SUITE /////////////// -//////// no need to modify these //////// -///////////////////////////////////////// - -// MAP -const fruitsWithKgMass = mapDict(fruits, (fruit, name) => ({ - ...fruit, - kg: 0.001 * fruit.mass, - name, -})) -const lemonName: string = fruitsWithKgMass.lemon.name -// @ts-ignore-error -const failLemonName: number = fruitsWithKgMass.lemon.name -assertOk(fruitsWithKgMass, '[MAP] mapDict returns something truthy') -assertEquals( - fruitsWithKgMass.cherry.name, - 'cherry', - '[MAP] .cherry has a "name" property with value "cherry"', -) -assertEquals( - fruitsWithKgMass.cherry.kg, - 0.005, - '[MAP] .cherry has a "kg" property with value 0.005', -) -assertEquals( - fruitsWithKgMass.cherry.mass, - 5, - '[MAP] .cherry has a "mass" property with value 5', -) -assertEquals( - Object.keys(fruitsWithKgMass).length, - 8, - '[MAP] fruitsWithKgMass should have 8 keys', -) - -// FILTER -// only red fruits -const redFruits = filterDict(fruits, (fruit) => fruit.color === 'red') -assertOk(redFruits, '[FILTER] filterDict returns something truthy') -assertEquals( - Object.keys(redFruits).length, - 4, - '[FILTER] 4 fruits that satisfy the filter', -) -assertEquals( - Object.keys(redFruits).sort().join(', '), - 'apple, cherry, grape, raspberry', - '[FILTER] Keys are "apple, cherry, grape, raspberry"', -) - -// REDUCE -// If we had one of each fruit, how much would the total mass be? -const oneOfEachFruitMass = reduceDict( - fruits, - (currentMass, fruit) => currentMass + fruit.mass, - 0, -) -assertOk(redFruits, '[REDUCE] reduceDict returns something truthy') -assertEquals( - typeof oneOfEachFruitMass, - 'number', - '[REDUCE] reduceDict returns a number', -) -assertEquals( - oneOfEachFruitMass, - 817, - '[REDUCE] 817g mass if we had one of each fruit', -) - -/**/ -export default {} diff --git a/packages/notes-ts-fundamentals-v4/tsconfig.json b/packages/notes-ts-fundamentals-v4/tsconfig.json index 2a9356700..dd7fa1d0a 100644 --- a/packages/notes-ts-fundamentals-v4/tsconfig.json +++ b/packages/notes-ts-fundamentals-v4/tsconfig.json @@ -1,14 +1,16 @@ { - "include": ["src"], - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist", - "target": "ES2018", - "moduleResolution": "Node", - "noUnusedParameters": false, - "noUnusedLocals": false, - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true - } -} \ No newline at end of file + "include": ["src"], + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "target": "ES2018", + "moduleResolution": "Node", + "noUnusedParameters": false, + "noUnusedLocals": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictBindCallApply": true, + "strictNullChecks": true, + "noImplicitOverride": true + } +} diff --git a/packages/welcome-to-ts/tsconfig.json b/packages/welcome-to-ts/tsconfig.json index 37a7e0162..0182b6368 100644 --- a/packages/welcome-to-ts/tsconfig.json +++ b/packages/welcome-to-ts/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { "outDir": "dist", - "target": "ES2015", - "moduleResolution": "node", + "rootDir": "src", + "target": "ES2022", + "module": "NodeNext" }, "include": ["src"] }