# Note - understanding TypeScript - 2022 Edition (C3~C8)
# Compiler and Config
`tsc app.ts -w // watch mode`,但如果有多個 ts 檔案呢?
watch mode for entire project folder and re-compile any ts file that might change
`tsc --init`: initial this project. 讓這份專案被 ts 管理,然後執行完指令後出現 tsconfig.jason -> 可以直接執行 `tsc`,complie all ts files,也可以和 watch mode 結合 `tsc -w`
tsconfig.jason 設定
compile inludes minus exclude
```jason
"exclude": [
// any file in this pattern in any folder will be ignored
"*.dev.ts",
"**/*.dev.ts",
"node_modules" // would be default
],
"include": [
// 沒有在這裡面的就不會被 compile
],
"files": [
// include specific files you want to compile
// 通常小型專案中你想指定特定想編譯的檔案
"app.ts"
]
```
## compiler options
```jason
"target": "ES6", // 你要用哪一版的 js 可以設定符合較舊瀏覽器的 js
"lib": [
// ES6 的 default
"DOM",
"ES6",
"DOM.Iterable",
"ScriptHost"
],
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
"checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
"jsx": "preserve", // for react
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
"rootDir": "./", /* Specify the root folder within your source files. */
"outDir": "./", /* Specify an output folder for all emitted files. */
"removeComments": true, // compile 成 js 拿掉註解,有助於檔案變小,
// "noEmit": true, /* Disable emitting files from a compilation. */
"downlevelIteration": true,
"noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
```
Q: ts 怎麼知道 document, const 這些東西?
lib - specify which default objects and features ts knows with that.
ts code 不是只能 write for browser, 也可以在 node.js applicatoin 寫,既然如此下方這段就不會作用了,但之所以下面這段可以作用是因為 lib.
如果 lib 沒有寫,就會帶入他的 default,default 取決於 js target,並且預設所有 DOM API 都是可用的
```ts
const button = document.querySelector('button')! // 告訴 ts button 是存在的
button.addEventListener('click',() => {
})
```
allowJs: 將 js file 納入 compile,儘管他不是 .ts 也會被 ts 編譯。透過 checkJs report potential error -> 這兩個參數設定的好處是,你可以寫原生的 js 但享受部分 ts 的好處
sourceMap: help debug and development
Q: 如果你的 ts 很複雜,你想 debug 的是 ts code 而不是 compile 好的 js code 你該怎麼做?
A: dev source 可以看到 .ts file will be nicer for us,將 `sourceMap: true` 會產生 .js.map,這類型的檔案是作為瀏覽器和開發者工具的中間橋樑,connect .js to the input file (.ts ),如此一來在 dev tool 也可以對 .ts 下斷點
outDir & rootDir: 專用越大就越需要好好管理檔案 - where input files live and output files generate
* dist folder - hold all the output
* src folder - hold all ts files
將 .ts files 移入 src 裡面,但若現在若重新 compile,src 就會出現 .js files sits next to our input files. `outDir: './dist'` outDir 的設定指定這些 compile 好的 .js 要放到哪裡去,此外 src 資料結構會被複製到 dist 去
rootDir 會設定你 output 的根目錄,超過根目錄的就不會理他了,和 include option 有類似的效果
noEmit: 如果不想 compile 任何 js 出來,有點怪但舉例來說大型專案,你想省時間只是想確認 ts 檔案有無寫對
downlevelIteration: when you compile your code to old version of js, and you work with for loop with some rare scenarios. 這種情況,打開他會給你一些關於 loop 的指示,但打開會產 verbose code。
noEmitOnError: 設定為 false,表示就算 ts 有錯,但仍會 compile 為 js,建議打開設為 true
## strict 系列
* noImplicitAny: 設定為 true 時,parameters 不能是 any 但 variable 沒差
* strictNullChecks: 告訴 ts regarding how you work and access with values that might potentially hold a null value
* strictFunctionTypes
* strictBindCallApply: which function you are calling, bind, call or apply, and it checks if what you're setting up here makes sense.
* strictPropertyInitialization
* alwaysStrict
```ts
let logger
// 不指定型別會:Parameter 'data' implicitly has an 'any' type.
const sendAnalytic = (data: string) => {
console.log(data)
logger = true
logger = '123'
}
sendAnalytic('Data')
```
workaround:
1. `!`: this operation will yield non null value
2. 若無法完全確定是否存在,可以結合 if
```ts
// ts 不會檢查 .html 去識別 button 是否存在,如果不存在就會回 null
// 然後下面的 addEventListener 就會失敗,為了預防這樣的問題,請設為 true
const button = document.querySelector('button')
button.addEventListener('click',() => {
console.log('Clicked')
})
-----
// workaround 1
const button = document.querySelector('button')!
-----
// workaround 2
if (button) {
button.addEventListener('click',() => {
console.log('Clicked')
})
}
```
And for some reason we want to make sure that when this executes, we pass in certain arguments or we set the this keyword in there to a certain value.
Now, since click handler is passed to add event listener like that so that the browser basically executes this for us, if we want to pre configure the arguments which will be passed in, we can use bind and bind as our first argument takes what we want to bind the this keyword to.
```js
const button = document.querySelector('button')
const clickHandler = (message: string) => {
console.log(`Click ${message}`)
}
if (button) {
button.addEventListener('click',() => {
console.log('Clicked', clickHandler.bind(null)) // don't use this for the function so bind null
})
}
// workaround - 傳入第二個參數,他會是妳想 pass 的第一個參數
if (button) {
button.addEventListener('click',() => {
console.log('Clicked', clickHandler.bind(null, 'haaa'))
})
}
```
```ts
"strict": true, // 會把底下所有 strict 相關的都設為 true
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
```
## Code Quality Options
* noUnusedLocals - 設為 true 時,function 宣告的變數沒有用到,ts 會報錯 ; 但對全域設定的變數 ts 是允許的,因為怕你其他地方要用到
* noUnusedParameters - 設為 true 時,沒使用到的參數 ts 也會報錯
* noImplicitReturns- 設為 true 時,不允許 function 有時 retrun 有時不 return
```js
const add = (a, b) => {
if (a + b > 0) {
return a + b
}
console.log('xxxx') // noImplicitReturns true 會報錯
}
```
```ts
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
```
## Debug
建議把 sourMap 也打開
```jason
// .vsCode launch.jason
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3001", // server running
"webRoot": "${workspaceFolder}" // project file will be the host file
}
]
}
```
---
> ts compile your code not only from ts only feature to regular js but also from modern js to old js if you tell js to do so. 可以指定 ts 要編譯完後的 js 版本
# ES6
差異在於作用域,let const 可以幫我們更 clean code
var - global and function scope
let - block scope
```js
if (true) {
var a = 2;
let b = 3;
}
console.log(a) // 2
console.log(b) // b is not defined
```
Default arguments are not skipped when you call a function
所以建議沒有 default 的引數要在前
```ts
const add = (inputOne: number, inputTwo: number = 2) => inputOne + inputTwo
console.log(add(2))
```
spread operator: put out all the elements of the array / object and add them as a list of value
```ts
const hobbies = ['Cooking', 'Dating']
const activeHobbies = ['Running'];
activeHobbies.push(hobbies[0], hobbies[1]) // push change the memory not the address.
// spread operator
const activeHobbies = ['Running', ...hobbies];
```
```ts
const person = {
name: 'Angela',
age: 14
}
// 不算拷貝成功!
const copiedPerson = person // copy the pointer of the person
// 淺拷貝
const copiedPerson = {...person} // key value pair pull out
```
rest parameters: where you want to accept as incoming value, 很適合用在你傳入的參數無限多時
```ts
// 擴充原本的 add function,我想傳幾個參數就傳幾個~
const add = (a, b, c, d) => {
}
console.log(add(1, 2, 3, 4)
// rest parameters will merge the incoming list of value into an array
const add = (...numbers: number[]) => {
return numbers.reduce((prevVal, curVal) => {
return prevVal + curVal
}, 0)
}
console.log(add(1, 2, 3, 4))
```
destructure: pull element out of the array or object 解構並不會改變原本的陣列或物件
```ts
const [hobbyOne, hobbyTwo, ...remainingHobbies] = hobbies
```
---
# Classes and Interfaces
- what & why
- classes and inheritance
- interfaces
- others:
[mdn classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
[ts class]([https://www.typescriptlang.org/docs/handbook/2/objects.html)
> What is Object-Oriented Programming?
Work with real-left entities in your code
classes & instances
```
objects
1. thins work with your code
2. build object based on classes ie 'Instances' of classes, 因此可以重複製作多個有相同結構的物件
3. class-based creation is an alternative to using object literals
------
classes
1. blueprint for objects,
2. def how objects should look like, which properties and methods they have
3. make creation of multiple similar objects much easier
```
## ex: 管理公司不同部門的 app
Need:
1. 不同的部門 -> 使用物件管理不同部門的資料,建立使用物件的方法使的 render department information on the screen
痛點:
1. 不同部門的物件構造相似,如何減少 creation 的工
解決方式:department class
Note:
* 慣例上 className 會大寫,`{}` 裡面叫做 field of class
* class 在 js 裡是 constructor function 的語法糖
* function in object are called method
設定 class 的初始值,會透過特殊的 function `method` 去做
constructor function ties to this class and ties to any object you create based on the class which is executed when the object is being created -> 可透過建構子函數對你構造出的物件做初始化
class field 會被轉化成物件的 property
```ts
class Department {
name: string; // def the name and value type you will have on object
constructor(n: string) {
this.name = n; // reach out to name by this keyword
// this: set the property ie the value of name field, to the value you are getting on end
// when the department object is created
}
}
const finance = new Department('Fianace'); // 透過 `new` create object
console.log(finance) // Department {name: 'Fianace'}
```
### compile to JS
```js
"use strict";
// es6
class Department {
constructor(n) {
this.name = n; // reach out to name by this keyword
// this: set the property ie the value of name field, to the value you are getting on end
// when the department object is created
}
}
const finance = new Department('Fianace');
console.log(finance); // Department {name: 'Fianace'}
//# sourceMappingURL=app.js.map
```
原生的 js 透過建構子函數與 `new`
```js
"use strict";
// es5
var Department = /** @class */ (function () {
// constructor function
function Department(n) {
this.name = n;
}
return Department;
}());
var finance = new Department('Fianace');
console.log(finance); // Department {name: 'Fianace'}
//# sourceMappingURL=app.js.map
```
## Constructor Functions & `this`
classes and Constructor: properties & method
名詞解釋:
* properties: define the general structure of the object
* constructor functions: 初始化 class 會被呼叫
Q: 如何對創建的物件調用時增加方法?
```ts
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe() {
console.log(`Department ${name}`) // 這樣寫調用會失敗
// 他找到的是 window object ie undefined
}
}
```
失敗的原因:
1. look for variable name inside the describe method or outside the class as a global variable
為了要指向 class
workaround: `this`
1. This then typically refers back to the concrete instance of this class that was created and there with the dot notation
`finance.describe()` -> this 會指向 finance object,
```ts
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe() {
console.log(`Department ${this.name}`)
}
}
const finance = new Department('Fianace');
finance.describe() // Department Fianace
```
原因:this 通常是指向呼叫他的 method,這裡的 this 是指 financeCopy,但 financeCopy 沒有 name 的 property
```ts
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
describe() {
console.log(`Department ${this.name}`)
}
}
const finance = new Department('Fianace');
finance.describe()
const financeCopy = { describe: finance.describe }
financeCopy.describe() // Department undefined
```
workaround: add parameter `this` (`this` is interpreted by TypeScript to be a hint regarding what this should be referred to.),這樣一來他的不是參數,你可以呼叫 describe() 在傳入任何東西
```ts
class Department {
name: string;
constructor(n: string) {
this.name = n;
}
// this alaways be the instance of department class
describe(this: Department) {
console.log(`Department ${this.name}`)
}
}
const finance = new Department('Fianace');
finance.describe()
const financeCopy = { name: 'Dummy', describe: finance.describe }
financeCopy.describe()
```
### Private & Public
Js 沒有這兩個屬性只有很新的 JS 有增加這兩個屬性
(Vanilla JS private field syntax don't use the private and public keyword,ts 支援是因為他是在 compilation 檢查而非 runtime )
```ts
class Department {
name: string;
employees: string[] = []
constructor(n: string) {
this.name = n;
}
// this alaways be the instance of department class
describe(this: Department) {
console.log(`Department ${this.name}`)
}
addEmployee(employee:string) {
this.employees.push(employee)
}
printEmployeeInformation() {
console.log(`${this.employees} ${this.employees.length}`)
}
}
const finance = new Department('Fianace');
finance.addEmployee('Max');
finance.addEmployee('Nick');
finance.printEmployeeInformation()
// 問題: 可以直接從外面改變 Class 的 property
finance.employees[2] = 'Hacker'
finance.printEmployeeInformation() // Max,Nick,Hacker 3
```
Besides property, you can also mark methods as private
設為 private,property 就只能在 class 內部被改動
public is default by ts, and public properties are accessible from outside
```ts
class Department {
public name: string;
private employees: string[] = []
constructor(n: string) {
this.name = n;
}
// this alaways be the instance of department class
describe(this: Department) {
console.log(`Department ${this.name}`)
}
addEmployee(employee:string) {
this.employees.push(employee)
}
printEmployeeInformation() {
console.log(`${this.employees} ${this.employees.length}`)
}
}
const finance = new Department('Fianace');
finance.addEmployee('Max');
finance.addEmployee('Nick');
finance.name = 'Angela'
finance.printEmployeeInformation() // Max,Nick,Hacker 3
```
### Shorthand Initialization
# Advanced Types
- intersection types
- type guards
- discriminated Unions
- type casting
- function overloads