# Note - understanding TypeScript - 2022 Edition ## 學習資源包 1. official website -> documentation & playground 2. project or ex: angular, nest.js baed on node js for the server side 3. google for typescript example --- > 什麼是 typescript ? TS is js superset ie a language building up on JS TS 增加新的功能與優勢在原本的 js 上,但有一個大大的缺點,TS 無法在 JS 環境上執行 (瀏覽器無法執行),但這不表示我們無法使用它,TS 是一個程式語言也是一個工具,可以編譯 TS to JS > TS 如何增加新的 feature 在 regular JS 上? TypeScript compiler compiles these new features to JavaScript workarounds. > TS 的優點? 型別檢查 課程大綱 ![](https://i.imgur.com/Yb5XRYr.jpg) * typeCasting let ts know which type it will be ts 會 compile 成 js 這種寫法原生的 js 是不能接受的 ```ts const button = document.querySelector("button"); const input1 = document.getElementById("num1")! as HTMLInputElement; // this will always find an element const input2 = document.getElementById("num2")! as HTMLInputElement; function add(num1: number, num2: number) { if (typeof num1 === "number" && typeof num2 === "number") { return num1 + num2; } else { return +num1 + +num2; } } button.addEventListener("click", function () { console.log(add(+input1.value, +input2.value)); }); ``` install node.js 就可以使用 npm command npm init 會產出 package.json --save-dev 只有開發環境會用到的 lite server 是簡易版的開發環境用的 server which always servers index.html file next to package.json # 核心語法與 feature TS 有超多型別,甚至可以做出屬於自己獨一無二的型別 > js 使用這些型別與 ts 使用這些型別的意義與差異為何? JS uses dynamic types(resolves at runtime), typescript uses static types(set during development) * JS 是動態地型別,所以在 runtime 我們會用 typeof 去檢查型別,目的是確保後續地代碼如果有依賴到相對應的型別 * TS 是靜態型別,他們不會在 runtime 改變型別.(ts 最終會編譯成 js,理論上是可做到 js 動態型別地效果.),可以在 development 時進行型別檢查,另外與 js 相比 type 更豐富 ## Core Type ![](https://i.imgur.com/K6goQyV.png) ```js const add = (num1, num2) => { return num1 + num2 // 102.4 } const number1 = '10' const number2 = 2.4 const result = add(number1, number2) console.log(result) ``` by adding a colon here after the value to which we want to assign a type. > TS 只在編譯的時候幫忙我們,他並沒有改變 JS 的運行,(瀏覽器是不能運行 TS 的,It can only help us during development before we compile our TypeScript code to JavaScript. 我們可以透過 JS 提供的 typeof 去檢查型別,但缺點是型別錯誤是在 runtime 才會報錯的 **The core primitive types in TypeScript are all lowercase!** ex: It is string and number (etc.), NOT String, Number etc. Note: 1. app.js & app.ts 同時打開,IDE 會報錯因為重複的 function 執行, 2. tsc fileName.js -> it compiles the typescript file and spits out a JavaScript file. --- # [Basic Type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html) ## primitive type ### Numnber & String & Boolean 1. ts js 都是一樣的,所有的 number 的預設值都是 floats ```ts const add = (num1:number, num2:number, showResult: boolean, phrase: string) => { if (showResult) { console.log(`${phrase} ${num1 + num2}`) } else { return num1 + num2 } } const number1 = 10 const number2 = 2.4 const printResult = true const resultPhrase = "result is: " const result = add(number1, number2, printResult, resultPhrase) ``` ## type inference ts understand which type you have in a certain variable or a constant. 可以在變數後面加 : 型別,但如果你有賦值給他,這其實是多餘且不好的寫法,因為 ts 可以去推斷你所使用的型別,除非初始化時沒有賦值給他 ```ts // bad let number1: number = 10 // good let number1: number number1 = 5; // ts inferred that we store a string // get error -> we store a number let resultPhrase = "result is: " resultPhrase =111 ``` ### object type ```ts // generic object type const person: object = { name: "Angela", age: 26 } // setting specific object type const person: { name: string; age: number; } = { name: "Angela", age: 26 } // nested obj { id: string; price: number; tags: string[]; details: { title: string; description: string; } } const product = { id: 'abc1', price: 12.99, tags: ['great-offer', 'hot-and-new'], details: { title: 'Red Carpet', description: 'A great carpet - almost brand-new!' } } ``` ### array type ```ts const person: { name: string; age: number; hobbies: string[] } = { name: "Angela", age: 26, hobbies: ['Cooking', 'Painting'] } for (const hobby of person.hobbies) { console.log(hobby.toUpperCase()) } ``` ---- ## Type script add not for vanilla JS ### Tuple fixed length and fixed types array push 是 tuple 的例外 ```ts const person: { name: string; age: number; hobbies: string[], role: [number, string] // tuple } = { name: "Angela", age: 26, hobbies: ['Cooking', 'Painting'], role: [2, 'teacher'] } person.role.push('admin') // 檢查會過 person.role = [1, 'doctor', 'Female'] // 會被 ts 擋下 ``` ### Enum having a couples of specific identifiers, global constants, you want to assign a human readable label 通常 enum 都是大寫,但這不是必要,只是慣例; enum default start at zero ```ts // 缺點是要定義這些常數並維護他們 const ADMIN = 0; const READ_ONLY = 1; const AUTHOR = 2 const person: { name: string; age: number; hobbies: string[], role: number, } = { name: "Angela", age: 26, hobbies: ['Cooking', 'Painting'], role: ADMIN } // 使用 enum 好處是閱讀性提升,另外可指定 mapped value enum Role { ADMIN = 10, READ_ONLY = 2, AUTHOR = 100 } const person: { name: string; age: number; hobbies: string[], role: number } = { name: "Angela", age: 26, hobbies: ['Cooking', 'Painting'], role: Role.ADMIN } ``` ### Any 沒事不要用到它 ### Union Type depend on your logic, some case need run time check ```ts const combine = (inputOne: number | string, inputTwo: number | string) => { let result: number | string; if (typeof inputOne === 'number' && typeof inputTwo === 'number') { result = inputOne + inputTwo } else { result = inputOne.toString() + inputTwo.toString() } return result } const combineAge = combine(20, 30); const combineName = combine('Amy', 'Nick') ``` ### Literal Type 使用時機:非常確信他的值 ![](https://i.imgur.com/EB5LtNj.png) ```ts const combine = (inputOne: number | string, inputTwo: number | string, resultConversion: string) => { let result: number | string; if (typeof inputOne === 'number' && typeof inputTwo === 'number' || resultConversion === 'as-number') { result = +inputOne + +inputTwo } else { result = inputOne.toString() + inputTwo.toString() } return result } // 缺點:不能傳錯值,可以用 enum 改善, // 但因為這邊只有兩個值,所以 literal type 會更合適 const combineAges = combine(20, 30, 'as-number'); console.log(combineAges) const combineStringAges = combine('20', '33', 'as-number') console.log(combineStringAges) const combineNames = combine('Amy', 'Nick', 'as-text') console.log(combineNames) ``` ```ts const combine = ( inputOne: number | string, inputTwo: number | string, resultConversion: "as-number" | "as-text" ) => { // 略 }; ``` ### Type Aliases / Custom Types with ts keyword `type` + 別名,將你想要封裝的型別寫在 aliases 裡面 ```ts // make reusable type aliases here!!! type CombinAble = number | string type ConversionDescriptor = "as-number" | "as-text" const combine = ( inputOne: CombinAble, inputTwo: CombinAble, resultConversion: ConversionDescriptor ) => { let result: CombinAble; if ( (typeof inputOne === "number" && typeof inputTwo === "number") || resultConversion === "as-number" ) { result = +inputOne + +inputTwo; } else { result = inputOne.toString() + inputTwo.toString(); } return result; }; ``` alias 也可以是 object ```ts type User = { name: string; age: number }; const u1: User = { name: 'Max', age: 30 }; type User = { name: string; age: number }; function greet(user: User) { console.log('Hi, I am ' + user.name); } function isOlder(user: User, checkAge: number) { return checkAge > user.age; } ``` ### Function #### Return return type is inferred by ts; 若有特地目的要指定 function 回傳型別可以這麼做 ```ts const add = (n1: number, n2: number): number => { return n1 + n2 }; ``` void -> function return nothing but not undefined 如果你使用 undefined, ts 會預期 return undefined ```ts const add = (n1: number, n2: number): undefined => { return }; const printResult = (num: number): void => { console.log(`Result is ${num}`) } ``` #### Type Function Types are types describe a function regarding the parameters and return value ```ts const add = (n1: number, n2: number): number => { return n1 + n2 }; const printResult = (num: number): void => { console.log(`Result is ${num}`) } // 缺點,沒有指定他是哪一個 function let combinValues: Function; combinValues = add combinValues = printResult // 出錯了 console.log(combinValues(1, 15)) // 改善 let combinValues: (a: number, b: number) => number; combinValues = add ``` ### cb 這邊的寫法 `=> void`,不是指強迫你 cb 不能回傳東西,而是說任何回傳的東西都不會被使用到 callback functions can return something, even if the argument on which they're passed does NOT expect a returned value. ```ts // cb: ignore any result you return const addAndHandle = (n1: number, n2: number, cb: (num: number) => void) => { const result = n1 + n2; cb(result); } addAndHandle(12, 30, (result) => { console.log(result) return 123 // 這也是為何沒有報錯 }) ``` ### Unknow Type 不該常用,但比 any 好,因為至少你還得做一些型別檢查,才能將 unknown 的值 assign 給 fixed type ```ts let userInput: unknown let userName: string userInput = 5; userInput = 'ABC' if (typeof userInput === 'string') { userName = userInput } ``` ### Never Type error is thrown -> it cancels our script,當然可以用 try catch 讓 script 繼續 never return anything ```ts // 使用時機:throw error / infinite loop const generateErr = (message: string, code: number): never => { throw({errorMessage: message, errorCode: code}) // while(true) {} } generateErr('Invalid XXX', 500) ```