Learning TypeScript Part 2 === ![](https://i.imgur.com/Ohu1K6L.png) --- ###### tags: `TypeScript` ## Recap of JS classes ### Initialize a project 1. Open terminal and `cd` to the directory you would like to initialize your project, i.e. desktop. `cd desktop` 2. Once you are in the desktop directory, create your project by using `mkdir`, i.e. `mkdir typescript` 3. `cd` to your project, i.e. `cd typescript`. 4. Inside your project folder, run `tsc init` to initialize TypeScript and also run `npm init -y`. 5. You shall have these files by now: `tsconfig.json`,`node_modules`, `package-lock.json` and `package.json`. 6. Create 2 folders by running `mkdir src dist` and a html file by running `touch index.html`. 7. Inside `index.html`, try tp attach `index.js` file at the bottom of `<body>` tag. 8. Go to `tsconfig.json`, find `outDir`, uncomment it and add `dist` as output directory. ![](https://i.imgur.com/DlGeA16.png) 9. Add `"include": ["src"]` at the bottom. ![](https://i.imgur.com/h2kBuTo.png) 10. Create `index.ts` inside the `src` folder and type several codes, i.e. `console.log('Good to go!')` 11. Run `tsc -w` to watch change for the ts file, by now you should be able to see it convert to normal js file inside the dist folder. ![](https://i.imgur.com/Xr44LGs.png) --- ### class keyword `class` keyword allows us to define an object and we use `new` keyword to instantiate it. ```javascript class Player{}; const player1 = new Player(); console.log(player1); // Player {} ``` Now inside the `Player`, we can add properties, i.e. a function or a simple information. ```javascript class Player{ cheering(){ console.log('Go team go!!') } }; const player1 = new Player(); player1.cheering(); // Go team go!! ``` --- ### constructors When we instantiate a new instance (i.e. `new Player()`), it would automatically be called. ```javascript class Player { constructor(){ console.log('Hello'); } } const player1 = new Player(); console.log('I am player11'); const player2 = new Player(); console.log('I am player 2'); ``` ![](https://i.imgur.com/58iirCj.png) Accepting arguments. ```javascript class Player { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } greeting() { console.log(`Hello, I am ${this.firstName} ${this.lastName}`) } } const player1 = new Player('Alex', 'Holland'); player1.greeting(); const player2 = new Player('Ben', 'Porter'); player2.greeting(); ``` ![](https://i.imgur.com/BZZxWXM.png) --- ### class fields Traditionally, if there're values that require to be initialize, i.e. player's lives and score, and everyone should have the same score and life in the beginning, we can wrtie inside of a constructor like below. ```javascript class Player { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.score = 0; this.numLife = 10; } } ``` Or we can use syntactic sugar as we can define them outside the constructor. ```javascript class Player { score = 0; numLife = 10; constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } greeting() { console.log(`Hello, I am ${this.firstName} ${this.lastName}`) } looseLife() { this.numLife -= 1; } } const player1 = new Player('Alex', 'Holland'); console.log(player1); player1.looseLife(); console.log(player1) ``` ![](https://i.imgur.com/vNYQFh0.png) --- ### Private fields From code above, we can easily to access `score` and `numLife` and change its value. We can add a underscore to say that the value should be in private, can't use outside the class, but this cannot prevent from changing the value either. ```javascript class Player { score = 0; // _score = 0; numLife = 10; // _numLife = 10; constructor(firstName, LastName) { this.firstName = firstName; this.LastName = LastName; } greeting() { console.log(`Hello, I am ${this.firstName} ${this.LastName}`); } looseLife() { this.numLife -= 1; } } const player1 = new Player('Tom', 'Holland'); player1.greeting(); console.log(player1); player1.numLife = 3; console.log(player1); ``` ![](https://i.imgur.com/uV5RdSe.png) We can use `#` to indicate as a private value so that we cannot access the value and change it. ```javascript class Player { #score = 0; #numLife = 10; } const player1 = new Player('Tom', 'Holland'); // This will show error msg player1.#numLife = 3; ``` ![](https://i.imgur.com/5nMkBpu.png) If we want to access wether `score` or `numLife`, we can setup a function to return value. ```javascript class Player { #score = 0; #numLife = 10; getScore() { return this.#score; } getNumLife() { return this.#numLife; } } const player1 = new Player('Tom', 'Holland'); console.log(player1.getScore()); console.log(player2.getNumLife()); ``` ![](https://i.imgur.com/oinXw7N.png) Now we can add more actions inside of `Player`, i.e. `updateScore()`. ```javascript= class Player { #score = 0; #numLife = 10; getScore() { return this.#score; } updateScore(newScore) { return this.#score = newScore; } getNumLife() { return this.#numLife; } } const player1 = new Player('Tom', 'Holland'); console.log(player1.getScore()); // 0 console.log(player1.getNumLife()); // 10 console.log(player1.updateScore(3)); // 3 ``` We can also add a private method inside of class. ```javascript class Player { #score = 0; #numLife = 10; getScore() { return this.#score; } updateScore(newScore) { return this.#score = newScore; } getNumLife() { return this.#numLife; } #call() { console.log('Call me!') } } ``` Since it's private, we can't call it outside of the class. ![](https://i.imgur.com/rzVn2aS.png) How can we call it? We can use constructor to make it to be called when instantiate an instance. ```javascript class Player { #score = 0; #numLife = 10; constructor() { this.#call(); } getScore() { return this.#score; } updateScore(newScore) { return this.#score = newScore; } getNumLife() { return this.#numLife; } #call() { console.log('Call me!') } } const player1 = new Player('Tom', 'Holland'); ``` After we instantiate a new instance, it will automatically be called. ![](https://i.imgur.com/GnAfvJ0.png) --- ### Getters We use `get` to define a method and add `get` keyword before it and this allows us to use it directly like a property. ```javascript class Player { #score = 0; #numLife = 10; constructor(firstName, LastName) { this.firstName = firstName; this.LastName = LastName; this.#call(); } get fullName() { return `${this.firstName} ${this.LastName}`; } get score() { return this.#score; } greeting() { console.log(`Hello, I am ${this.firstName} ${this.LastName}`); } looseLife() { this.numLife -= 1; } #call() { console.log('Call me'); } } const player1 = new Player('Tom', 'Holland'); console.log(player1.fullName); // Tom Holland console.log(player1.score); // 0 ``` --- ### Setters We can use `set` to add extra logic in order to update the value, and it would be just like using `get` as we can treat them like property. ```javascript class Player { #score = 0; #numLife = 10; constructor(firstName, LastName) { this.firstName = firstName; this.LastName = LastName; this.#call(); } get fullName() { return `${this.firstName} ${this.LastName}`; } get score() { return this.#score; } set score(newScore) { if (newScore < 0) { throw new Error('Score must be positive'); } this.#score = newScore; } } const player1 = new Player('Tom', 'Holland'); console.log(player1.score); // 0; player1.score = -1; // Show error msg console.log(player1.score); ``` ![](https://i.imgur.com/jLffU2b.png) --- ### Getters & setters recap They are **accessor properties**, they are essentially functions that execute on getting and setting a value. ```javascript class Player { #score = 0; get score() { return this.#score; } set score(newScore) { if (newScore < 0) { thorw new Error('Score must be positive'); } this.#score = newScore; } } const player1 = new Player(); player1.score; player1.score = 2; ``` The getter works when `player1.score` is read, the setter – when `player1.score = 2` is assigned. For accessor properties, there is **no value** or **writable**, but instead there are get and set functions. That is, an accessor descriptor may have: 1. **get** – a function without arguments, that works when a property is read, 2. **set** – a function with one argument, that is called when the property is set, 3. **enumerable** – same as for data properties, 4. **configurable** – same as for data properties. > Reference: [Property-accessors](https://javascript.info/property-accessors) --- ### Static properties & methods We can assign a method to a class as a whole by using `static`. Usually, static methods are used to implement functions that belong to the class as a whole, but not to any particular object of it. ```javascript class Player { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } static greeting() { console.log(`Hello, I am ${this.firstName} ${this.lastName}`) } } const player1 = new Player('Even', 'Logan'); ``` Instance player1 does not have function `greeting()` and it cannot access function `greeting()` either. ![](https://i.imgur.com/g3iJgdy.png) class Player does have function `greeting()` and it can access function `greeting()` as well. ![](https://i.imgur.com/wI9iNtq.png) ![](https://i.imgur.com/KZplPLO.png) --- ### extends class We can use `extends` to inherit a parent class, and add its own property. ```javascript! class SuperPlayer extends Player { stop() { console.log('Pause the game') } }; const admin = new SuperPlayer(); ``` `admin` has every property that inherit from class `Player` with its own property which is function `stop()`. --- ### super() when we use `extend` to inherit a class, we don't have any constructor inside of child class, what if we add one, what would happen? ```javascript! class SuperPlayer extends Player { constructor(auths) { this.auths = auths; } stop() { console.log('Pause the game'); } }; const admin = new SuperPlayer(['pause', 'continue', 'determinate']) ``` ![](https://i.imgur.com/Fiijy2M.png) Since `class SuperPlayer` is inherited from `class Player`, if we want to add constructor inside of `class SuperPlayer`, we need to call `super()` first, let's say we also want to make `class SuperPlayer` takes `firstName` and `lastName`, we don't need to write like this. ```javascript! class SuperPlayer extends Player { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } } ``` We just use `super()` and add its own properties. ```javascript! class SuperPlayer extends Player { constructor(firstName, lastName, auths) { super(firstName, lastName); this.auths = auths; } } const admin = new SuperPlayer('Andy', 'Wooo', ['Pause', 'continue', 'determinate']); ``` ![](https://i.imgur.com/aRRtCp0.png) --- ## class in TypeScript ### class If we don't specify types for `firstName` and `lastName`, we will get errors right away. ![](https://i.imgur.com/WSPlzHV.png) Now we have added types for those arguments, but still got error. ![](https://i.imgur.com/AMSvAxe.png) In order to eliminate errors, we will need to specify types before contructor. ![](https://i.imgur.com/aOaCDtu.png) --- ### class fields in TS Just like in JavaScript, we can specify a property called `score`, and assign `0` without specify the type simply because TS will automatically assume its type as `number`, but we can specify just to be clear. Here we can see even we did not specify the type, it wouldn't show any error. ![](https://i.imgur.com/GQ2xCCJ.png) Here we specify the type for the `score` to be either `number` or `string`, you can see that the error occured because we gave a value of boolean. ![](https://i.imgur.com/LJ2LJWz.png) --- ### readonly property In TS, there's a special property called `readonly`, if we specify `readonly` before a property, it means that we can't re-assign a value to the property. Here we can see the error message `Cannot assign to 'lastName' because it is a read-only property.` ![](https://i.imgur.com/8F6MlVb.png) --- ### Public modifier Technically, every properties and methods are public inside of a class, this mean that we can access from anywhere outside of a class. With `readonly`, even thogh we can't re-assign a value to properties `firstName` or `lastName`, but we still can access it, it's just we can't give them a new value. We can add `public` keyword just to be clear to others to indicate that these values are public. ![](https://i.imgur.com/6ja33EP.png) --- ### Private modifier In TS, there are two kinds of modifiers, one is `public`, and another one is `private`, which means that we can't access a private property or method outside of a class. Here we add `private` modifier before `score`, so when we tried to access `score`, it showed error. ![](https://i.imgur.com/3UXeEgK.png) We can use JavaScript syntax, which is hashtag `#` to define a private value, but this only works in ES 2015 and higher. ![](https://i.imgur.com/5HXD67y.png) We can change it by modifying inside of `tsconfig.json`. ![](https://i.imgur.com/OkAfDeM.png) Once we changed the version of ES, TS didn't show error when using `#` ![](https://i.imgur.com/C6vRndh.png) --- ### Parameter properties shorthand It seems cumbersome if we specify types like we did from code above. There's another way of doing that which is called `parameter properties`, this mean we can define inside of a constructor. ![](https://i.imgur.com/XdxIm0q.png) ![](https://i.imgur.com/1TyQ8QE.png) We can define a private property inside of it as well. ![](https://i.imgur.com/A9mvf3L.png) --- ### Getter & setter We can use `get` and `set` in TS as well, but if we only have `get` with a method, it would be `read-only` by default. Here we only define a getter called `fullName` ```typescript= class Player { constructor( public firstName:string, public lastName:string, private score: number ){} get fullName(): string { return `${this.firstName} ${this.lastName}`; } } ``` ![](https://i.imgur.com/ufvKt2x.png) Now that's make a setter to accept new score. ```typescript= class Player { constructor( public firstName:string, public lastName:string, private _score: number ){} get fullName(): string { return `${this.firstName} ${this.lastName}`; } set score(newScore: number): void { if (newScore < 0) { throw new Error('Score must be positive'); } this._score = newScore; } } ``` TS doesn't allow any return type annotations for setter, even we added `void` to tell TS that this won't return anything, it still showde error, therefore we need to remove `void` type in setter. ![](https://i.imgur.com/YFT9uIS.png) Now, that's try it. ```typescript= class Player { constructor( public firstName:string, public lastName:string, private _score: number ){} get fullName(): string { return `${this.firstName} ${this.lastName}`; } set score(newScore: number): void { if (newScore < 0) { throw new Error('Score must be positive'); } this._score = newScore; } } const player1 = new Player('Andy', 'Holland', 0); player1.score = -3; ``` ![](https://i.imgur.com/vxKIgAp.png) ```typescript= class Player { constructor( public firstName:string, public lastName:string, private _score: number ){} get fullName(): string { return `${this.firstName} ${this.lastName}`; } set score(newScore: number): void { if (newScore < 0) { throw new Error('Score must be positive'); } this._score = newScore; } } const player1 = new Player('Andy', 'Holland', 0); player1.score = 77; ``` ![](https://i.imgur.com/rRcTLZr.png) --- ### Protected modifier **protected modifier** comes in when we are working with inheritance. Let's say when we create a parent class, if we use `protected` modifier for one of properties, any child class can be accessed, but if we change the modifier from `protected` to private, then a child class can't be accessed. ```typescript! class Player { constructor( public firstName:string, public lastName:string, private _score: number ){} ... } class SuperPlayer extends Player { isAdmin: boolean = true; maxScore() { this._score = 999; } } ``` ![](https://i.imgur.com/AWfpfxC.png) Once we modify `private _score: number` to `protected _score: number`, TS won't show any error. > [Access modifiers](https://www.typescripttutorial.net/typescript-tutorial/typescript-access-modifiers/) --- ### Classes and interfaces We can create properties by using `interface`, and use `implements` to add the properites, if we have multiple interfaces, we can seperate them by using comma. ```typescript! interface ColorSetting { color: string; } interface Print { print(): void; } class Bike implements ColorSetting { constructor(public color: string) { } } class Jacket implements ColorSetting, Print { constructor(public color: string, public brand: string) { } print(): void { console.log(`I bought a ${this.color} ${this.brand} jacket`); } } const myBike = new Bike('red'); const myJacket = new Jacket('Navy Blue', 'Coatch'); myJacket.print(); ``` ![](https://i.imgur.com/eRq2SIj.png) --- ### Abstract class >[TS- abstract classes](https://www.typescripttutorial.net/typescript-tutorial/typescript-abstract-classes/) An abstract class **cannot** be instantiated directly, typically, an abstract class contains one or more abstract methods, like `getSalary()`. ```typescript! abstract class Employee { constructor(private firstName: string, private lastName: string) { } abstract getSalary(): number get fullName(): string { return `${this.firstName} ${this.lastName}`; } compensationStatement(): string { return `${this.fullName} makes ${this.getSalary()} NTD` } } const employee = new Employee('John', 'Smith'); ``` Because an abstract class cannot be instantiated, here comes an error. The `getSalary()` method is an abstract method. The derived class will implement the logic based on the type of employee. ![](https://i.imgur.com/buDWiZ3.png) ```typescript! class FullTimeEmployee extends Employee { constructor(firstName: string, lastName: string, private salary: number) { super(firstName, lastName) } getSalary(): number { return this.salary; } } class PartTimeEmployee extends Employee { constructor(firstName: string, lastName: string, private rate: number, private hours: number) { super(firstName, lastName) } getSalary(): number { return this.rate * this.hours; } } const fullTimeEmployee1 = new FullTimeEmployee('John', 'Smith', 40000); const partTimeEmployee1 = new PartTimeEmployee('John', 'Doe', 190, 90); console.log(fullTimeEmployee1.compensationStatement()); console.log(partTimeEmployee1.compensationStatement()); ``` ![](https://i.imgur.com/0l5Wlix.png)