Try   HackMD
tags: Typescript

Typescript tutorial

youtube教學

Compiling TypeScript

  • tsc filename.ts: 將ts檔 Compiling to TypeScript, 每次修改完code都要執行一次才會重新 Compiling
  • tsc src/filename.ts: 如果檔案不在目前terminal所在路徑, 可以再加src路徑, 作用同上
  • tsc filename.ts -w: 修改完code 會即時將ts檔 Compiling to TypeScript, 可console看即時更新結果

Type Basics 2~6

//string, number, boolean let name:string; let age: number; let isOpen: boolean; //array let arr1: string[] = []; //array內value只能是string let arr2: number[] = []; //array內value只能是number let arr3: any[] = []; //array內value可以是任意型別 //union types let mixed: (string|number|boolean)[] = [] let uuid: string|number; uuid = 'emma'; uuid = 123; //object //任意物件內容都可以 let obj: object; obj = {name:'emma', age:123}; //限定物件key與value型別 let objDetail:{ name:string, age: number, isDrive: boolean, }; objDetail = { name: 'emma', age: 123, isDrive: false, }; // 物件內value型別可以任意 let obj = {name: any, age: any}; obj = { name: 'emma', age: 123 }; // 任意型別 let age: any = 25; age = true;

Better Workflow & tsconfig 7, Modules 14

  • terminal cd 到 src file
  • tsc --init在src資料夾會出現 tscconfig.json
// tscconfig.json { "compilerOption": { "target": "es6", //指定編譯生成的JS版本 "module": "commonjs", //指定生成哪種模組 ex:es2015 "rootDir": "./src" //.ts檔案所在位置 "outDir": "./public": // .ts要compiler放到哪裡 "forceConsistentCasingInFileNames": true, //強制區分大小寫, 默認為false "files": ["hello.ts"], //屬性指定要編譯的 TS 檔案 "include": ["src"]["src/**/*.ts"], //屬性設定編譯時包含哪些檔案或資料夾, 路徑可調整 "exclude": ["node_modules"], //屬性設定編譯時排除哪些檔案或資料夾 }, } // *-表示匹配0至多個字元(不包含分隔符號) // ?-匹配一個相符字元(不包含分隔符號) // **/-表示匹配所有子資料夾 //ps:以下code可以過, 但如果forceConsistentCasingInFileNames為true時, 會報錯誤:'2a' !== '2A' //2a.ts export const PI = 3.1415926; //1a.ts import PI from './2A.ts'; function fun(){ return PI; }
  • tsc -w 在vscode的filename.ts檔案, 下這個指令tsc -w 開啟同步compiler, 當改code的時候, 就會同步輸出js檔, 只要一更改ts就會馬上改js
    tscconfig.json設定參考

Function Basics 8

//宣告型別為 Function let greet: Function; greet = () => { console.log('hello world'); } //宣告function的參數型別 //有參數c時宣告型別, 並且有 defaultValue 10 const add = (a:number, b:number, c:number | string = 10) => { console.log(a+b); console.log(c); //如果沒有= defaultValue 10, log結果為 undefind } add(5,10, 'emma'); //有參數c時宣告型別, 且c不能=預設 defaultValue 10 const add = (a:number, b:number, c?:number | string) => { console.log(a+b); console.log(c); //如果沒有= defaultValue 10, log結果為 undefind } add(5,10, 'emma'); //定義function沒有回傳value: void const plusFn = (a:number, b: number, c?:number):void =>{ console.log(a+b); console.log(c); } const plusFn = (a:number, b: number, c?:number):number =>{ return a+b+c; }

Type Aliases 9

//用type宣告型別 type StringOrNum = string|number; type ObjWithName = {name:string, uid:StringOrNum}; const loginFn = (uid:StringOrNum, name:string) => { console.log(`${name} has a uid is ${uid}`); } const helloFn = (user:ObjWithName) => { console.log(`${user.name} say hello!`); }

Function Signatures 10, 宣告function三種例子, 參數與回傳值型別的差異

  • 宣告function型別:
let greet: Function; //example1: 宣告greet變數有兩個參數, 並定義型別, 與回傳值型別 let greet: (a:string, b:string) => void; greet = (name:string, greeting:string):void => { console.log(`${name} says ${greeting}`); } // example2: 如果有宣告回傳值則邏輯結束一定要回傳符合型別的value let cal: (a:number, b:number, action:string) => number; //回傳number cal= (a:number, b:number, action:string) => { //if 沒有else會報錯, 一定要回傳number if(action === 'add') { return a+b }else { return a-b } } // example3: 參數是物件 let loginFn: (obj: {name: string, age:number}) => void; type UserType = {name: string, age:number}; loginFn = (user:UserType) => { console.log(`${user.name} is ${user.age} years old.`); }

The DOM & Type Casting 11: 宣告DOM的型別有哪些

//example1: HTMLFormElement or HTMLAllCollection....等會有提示, 如下: //<form> id="type" const form = document.querySelect('.main') as HTMLFormElement; console.log(form.children); //example2: //<select> id="type" const type = document.querySelect('#type') as HTMLSelectElement; //<input> id="type" const type = document.querySelect('#type') as HTMLInputElement; //<form> id="type" const type = document.querySelect('#type') as HTMLFormElement;

Classes 12, Public, Private & Readonly 13

class Invoice { readonly client:string; //不管class內外都只能讀, 不能重新assign value private detail:string; //只能在class裡面access和賦值 public amount:number; //class外也可以access和賦值 constructor(c: string, d: string, a:number){ this.client = c; this.detail = d; this.amount = a; } //另一種寫法 constructor( readonly client:string; private detail:string; public amount:number; ){} format(){ // this.client = 'something else' // error, 因為client是readonly不能重新賦值 return `${this.client} owes ${this.detail} for ${this.amount}` } } const invOne = new Invoice('mario', 'work on the mario website', 250); const invTwo = new Invoice('luigi', 'work on the mario website', 300); //class就是一種型別 let invoices: Invoice[] = []; //class定義型別 invoices.push(invOne); invoices.push(invTwo); invoices.forEach(inv =>{ //因為inv.detail是private所以不能在class外存取, 但可從inv.format()取到detail的值, 因為format()是在class裡面access detail console.log(inv.client, inv.detail, inv.amount, inv.format()); })

Interfaces 15

//interface定義型別, 名稱要大寫開頭 interface User { name: string; age: number; speak(a: string): void; spend(b: number): number; } // 宣告userA的型別為User const userA: User = { name: 'emma', age: 123, speak(text: string):void { console.log(text); }, spend(amount: number): number { console.log(amount); return amount; } } //用Interfaces宣告function argument 型別範例 const userFn = (users:User) => { console.log('hello', person.name); } userFn(userA); //執行userFn

Interfaces with Classes 16: class如何使用Interfaces

// src/interfaces/HasFormatter.ts // 定義format回傳字串 export interfaces HasFormatter { format(): string; } // src/classes/Invoice.ts import { HasFormatter } from '../interfaces/HasFormatter.ts'; export class Invoice implements HasFormatter { constructor( readonly client:string; //不管class內外都只能讀, 不能重新assign value private detail:string; //只能在class裡面access和賦值 public amount:number; //class外也可以access和賦值 ){} format(){ // this.client = 'something else' // error, 因為client是readonly不能重新賦值 return `${this.client} owes ${this.detail} for ${this.amount}` } }

Rendering an HTML Template 17

  • 參照react寫法即可

Generics(泛型) 18

  • Generics(泛型)很重要的一點,就是讓我們寫的方法可以適用在不同的型別,而非只能使用在單一型別。
  • 定義泛型
// type type Dict<T> = { value: T; }; // interface interface WrappedValue<T> { value: T; } // arrow function const identity = <T>(x: T): T => x; // function function identity<T>(x: T): T { return x; }
  • 範例如下:可以dynamic defind data type也可以重複使用uid, name, errorCode
interface Resource<T> { data: T; //can dynamic defind uid: number; //repeact use name: string; //repeact use errorCode: number; //repeact use } const docObj:Resource<object> = { data: {name: 'emma'}; uid: number; name: string; errorCode: number; } const docStrArr: Resource<string[]> ={ data: ['emma', 'mark', 'lisa']; uid: number; name: string; errorCode: number; }


Generic範例: type可以重複使用就看傳入的type是什麼1
Generic範例: type可以重複使用就看傳入的type是什麼2

帶有限制的泛型 Type Parameter with Constraints:透過 extends 的使用,可以建立帶有**「限制」**的泛型:

interface WrappedValue<T extends string> { value: T; } // ⭕️ T 滿足 string 的型別 const val: WrappedValue<'Aaron' | 'PJ'> = { value: 'Aaron', }; // ❌ T 不滿足 string 時會噴錯 // Type 'number' does not satisfy the constraint 'string'. const val: WrappedValue<number> = { value: 30, }; // ❌ 因為沒有給 T 預設值,所以不能留空 // Generic type 'WrappedValue<T>' requires 1 type argument(s). const val: WrappedValue = { value: 30, };
  • 實際的例子像這樣:
// 接收 array: T[] 做為參數,回傳 Dictionary {[k: string]: T} // 若把 T 的限制 { id: number } 拿掉的話,將會沒辦法確定 T 是帶有 id 的物件 const arrayToDict = <T extends { id: number }>(array: T[]): { [k: string]: T } => { const out: { [k: string]: T } = {}; array.forEach((val) => { out[val.id] = val; }); return out; };

帶有預設值的泛型 Generic parameter defaults

  • 可以給泛型預設值,舉例來說,下面的程式碼指的是:沒有給 T 的話,T 預設的型別會是 string
interface WrappedValue<T = string> { value: T; }
  • 但如果只是定義預設值的話,使用者可以任意更改該型別,例如:
// 預設 WrappedValue 中使用的型別是 string,但使用者可以改成自己想要使用的 const val: WrappedValue<number> = { value: 30, };
  • 這時候,可以透過 extends 限制使用者給定的泛型,例如:

    • 如果沒有提供 T 則預設該型別是 string
    • 如果有給 T
      • T 需要滿足 string,否則會噴錯
      • 當 T 滿足 string 時,T 會變成變成實際代表的型別
interface WrappedValue<T extends string = string> { value: T; } // 如果沒有提供 `T` 則預設該型別是 `string` const val: WrappedValue = { value: 'hello', }; // ❌ 如果有給 T,但 T 不滿足 string 時,會噴錯 // Type 'number' does not satisfy the constraint 'string'. const val: WrappedValue<number> = { value: 30, }; // ⭕️ 如果有給 T,當 T 滿足 string 時,T 會變成變成實際代表的型別 const val: WrappedValue<'Aaron' | 'PJ'> = { value: 'Aaron', };

pjchender: generic進階用法

Enums 19

  • 基本跟在function component外宣告const物件類似
  • 差異是 enums 是常數希望不能被更改, 提高程式可讀性
  • 範例: value可以是數字or字串
enum requestStatusCodes { error = 400, success = 200, } enum requestWrongCodes { missingParameter = 'A', wrongParameterType = 'B', invalidToken = 'C', }

TypeScript | 善用 Enum 提高程式的可讀性 - 基本用法 feat. JavaScript

Tuples 20

let values: [string, string, number]; // defind type values = [obj.name, obj.address, obj.age]; //declare value newValues = [...]; // 解壓縮前要defind type才不會報錯

斷言 as unknown

  • 通常api拿回來的資料ts無法事先知道, 因此可以用as斷言人工定義會來的資料會是什麼類型
type Data ={ userId: number, id: number, title: string, completed: boolean, } async function getData() { const res = await fetch('https://jsonplaceholder.typicode.com/todo/1'); const data = await res.json() as Data; }

Record<key, value>

  • 有時候只知道key跟value的型別
interface CatInfo { age: number; breed: string; } type CatName = "miffy" | "boris" | "mordred"; const cats: Record<CatName, CatInfo> = { miffy: { age: 10, breed: "Persian" }, boris: { age: 5, breed: "Maine Coon" }, mordred: { age: 16, breed: "British Shorthair" }, };

Pick<Type, Keys>

  • 只需要某個type裡面某幾個項目, 篩選出某幾項
interface Todo { title: string; description: string; completed: boolean; } type TodoPreview = Pick<Todo, "title" | "completed">; const todo: TodoPreview = { title: "Clean room", completed: false, };

Omit<Type, Keys>

  • 省略掉某個type的某幾項, 省略掉某幾項
interface Todo { title: string; description: string; completed: boolean; createdAt: number; } type TodoPreview = Omit<Todo, "description">; const todo: TodoPreview = { title: "Clean room", completed: false, createdAt: 1615544252770, }; type TodoInfo = Omit<Todo, "completed" | "createdAt">; const todoInfo: TodoInfo = { title: "Pick up kids", description: "Kindergarten closes at 5pm", };

react component參數寫法

type UsersDataType = { data: { total: number; users: UserType[]; factories: GroupType[]; departments: GroupType[]; groups: GroupType[]; }; } type PropType = { data: UsersDataType; updateTable: Function; }; //用在function component的參數方法 const App: React.FC<PropType> = ({data, updateTable}) => { return<div>首頁</div> }

Wrap Up 21略