From 774ae2c45a2571bee18788cdd0e9538da03fb376 Mon Sep 17 00:00:00 2001 From: Woody Chang <42999338+kazettique@users.noreply.github.com> Date: Mon, 14 Oct 2024 23:03:07 +0800 Subject: [PATCH] [typescript/zh-tw] translate (#5142) --- zh-tw/typescript-tw.html.markdown | 286 ++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 zh-tw/typescript-tw.html.markdown diff --git a/zh-tw/typescript-tw.html.markdown b/zh-tw/typescript-tw.html.markdown new file mode 100644 index 00000000..f9ac30a0 --- /dev/null +++ b/zh-tw/typescript-tw.html.markdown @@ -0,0 +1,286 @@ +--- +language: TypeScript +contributors: + - ["Philippe Vlérick", "https://github.com/pvlerick"] + - ["Kiwimoe", "https://github.com/kiwimoe"] +translators: + - ["Woody Chang", "https://github.com/kazettique"] +filename: learntypescript-zh-tw.ts +lang: zh-tw +--- + +TypeScript 是為開發大型 JavaScript 應用程式而設計的語言。它為 JavaScript 導入某些程式語言常見的一些概念,諸如:類別(class)、模組(module)、介面(interface)、泛型(generic type)和靜態型別(static type)。TypeScript 是 JavaScript 的「超集」(superset):意即建立在 JavaScript 的基礎上,所有 JavaScript 語法皆可在 TypeScript 中使用。因此,TypeScript 可以無縫導入到任何 JavaScript 專案中。TypeScript 編譯器最終會編譯成 JavaScript 程式碼。 + +本文將只專注於 TypeScript 的額外語法,其他請參考 [JavaScript 的指南](/docs/javascript-tw) + +要測試 TypeScript 的編譯器,請前往 [Playground](https://www.typescriptlang.org/play),在那裡你可以輸入程式碼,獲得自動完成(autocomplete)功能,並查看編譯過的 JavaScript 程式碼。 + +```ts +// TS 基本型別有三種 +let isDone: boolean = false; +let lines: number = 42; +let name: string = "Anders"; + +// 當變數有賦值時,也可以省略型別定義 +let isDone = false; +let lines = 42; +let name = "Anders"; + +// 若無法確定型別,則可以定義為 `any` +let notSure: any = 4; +notSure = "maybe a string instead"; +notSure = false; // 布林值也屬於 `any` 型別 + +// 以 `const` 關鍵字定義常數 +const numLivesForCat = 9; +numLivesForCat = 1; // 報錯,常數初始化之後,無法指定新值 + +// 關於集合類型的資料,有型別化陣列(typed array)和泛型陣列(generic array) +let list: number[] = [1, 2, 3]; +// 或使用泛型陣列類型 +let list: Array = [1, 2, 3]; + +// 列舉型別: +enum Color { Red, Green, Blue }; +let c: Color = Color.Green; +console.log(Color[c]); // "Green" + +// `void` 用於函式不回傳任何值的特殊情況 +function bigHorribleAlert(): void { + alert("I'm a little annoying box!"); +} + +// 函式是一等公民,支援 lambda「胖箭頭」 `=>` 語法,並使用型別推斷 + +// 以下幾種函式寫法是等效的,編譯器會生成相同的 JavaScript 程式碼 +// 一般的函式 +let f1 = function (i: number): number { return i * i; } +// 自動推斷回傳型別 +let f2 = function (i: number) { return i * i; } +// 使用胖箭頭語法 +let f3 = (i: number): number => { return i * i; } +// 胖箭頭語法(自動推斷回傳型別) +let f4 = (i: number) => { return i * i; } +// 胖箭頭語法(自動推斷回傳型別、省略函式的括號與 `return` 關鍵字) +let f5 = (i: number) => i * i; + +// 函式的參數也可以同時定義多種型別的連集 +function f6(i: string | number): void { + console.log("The value was " + i); +} + +// 介面是結構化的,任何擁有這些屬性的物件都要符合該介面的定義 +interface Person { + name: string; + // 以問號(`?`)來表示選填的屬性 + age?: number; + // 當然也可以包含函式 + move(): void; +} + +// 實作 `Person` 介面的物件 +// 可被視為一個 `Person` 物件,因為它具有 `name` 和 `move` 屬性 +let p: Person = { name: "Bobby", move: () => { } }; +// 包含選填屬性的物件: +let validPerson: Person = { name: "Bobby", age: 42, move: () => { } }; +// 此物件非 `Person` 物件,因為 `age` 屬性非數字 +let invalidPerson: Person = { name: "Bobby", age: true }; + +// 介面也可以描述一個函式的型別 +interface SearchFunc { + (source: string, subString: string): boolean; +} +// 函式的型別定義著重於各個參數以及回傳值的型別,而函式名稱並不重要 +let mySearch: SearchFunc; +mySearch = function (src: string, sub: string) { + return src.search(sub) != -1; +} + +// 類別的屬性,其存取權限預設為公開(public) +class Point { + // 定義屬性 + x: number; + + // 在建構函式種使用 `public`、`private` 關鍵字,會實例化的時候自動生成屬性 + // 以此為例,`y` 如同 `x` 定義其屬性,並於實例化時賦值,但寫法更為簡潔,同時支援預設值 + constructor(x: number, public y: number = 0) { + this.x = x; + } + + // 函式,在類別中,又稱為方法(method) + dist(): number { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + + // 靜態成員(static member) + static origin = new Point(0, 0); +} + +// 類別可以被明確標記為實作某個介面。 +// 任何缺少的屬性或方法都會在編譯時引發錯誤。 +class PointPerson implements Person { + name: string + move() {} +} + +let p1 = new Point(10, 20); +let p2 = new Point(25); // y 值將預設為 0 + +// 類別的繼承 +class Point3D extends Point { + constructor(x: number, y: number, public z: number = 0) { + super(x, y); // 必須明確呼叫父類別的建構函式,使用 `super` 關鍵字 + } + + // 複寫父類別的方法 + dist(): number { + let d = super.dist(); + return Math.sqrt(d * d + this.z * this.z); + } +} + +// 模組,以 `.` 語法存取子模組 +module Geometry { + export class Square { + constructor(public sideLength: number = 0) { + } + area() { + return Math.pow(this.sideLength, 2); + } + } +} + +let s1 = new Geometry.Square(5); + +// 引用模組,可以在本地使用別名命名並使用之 +import G = Geometry; + +let s2 = new G.Square(10); + +// 泛用型別,泛型(generic type) +// 在類別使用泛型 +class Tuple { + constructor(public item1: T1, public item2: T2) { + } +} + +// 在介面使用泛型 +interface Pair { + item1: T; + item2: T; +} + +// 在函式使用泛型 +let pairToTuple = function (p: Pair) { + return new Tuple(p.item1, p.item2); +}; + +let tuple = pairToTuple({ item1: "hello", item2: "world" }); + +// 引用型別定義檔: +/// + +// 樣板字串(template string)(使用反引號「`」的字串) +// 以樣板字串進行字串內插(interpolation) +let name = 'Tyrone'; +let greeting = `Hi ${name}, how are you?` +// 多行的樣板字串 +let multiline = `This is an example +of a multiline string`; + +// 唯讀存取子:TypeScript 3.1 的新語法 +interface Person { + readonly name: string; + readonly age: number; +} + +var p1: Person = { name: "Tyrone", age: 42 }; +p1.age = 25; // 錯誤,`p1.age` 為唯讀屬性 + +var p2 = { name: "John", age: 60 }; +var p3: Person = p2; // 正確,`p2` 的唯讀別名 +p3.age = 35; // 錯誤,`p3.age` 為唯讀屬性 +p2.age = 45; // 正確,但因為 `p3` 參照可 `p2`,因此 `p3.age` 將會被修改 + +class Car { + readonly make: string; + readonly model: string; + readonly year = 2018; + + constructor() { + this.make = "Unknown Make"; // 唯讀屬性在建構函式被允許賦值 + this.model = "Unknown Model"; // 唯讀屬性在建構函式被允許賦值 + } +} + +let numbers: Array = [0, 1, 2, 3, 4]; +let moreNumbers: ReadonlyArray = numbers; +moreNumbers[5] = 5; // 錯誤,陣列的成員為唯讀 +moreNumbers.push(5); // 錯誤,無 `push` 方法(因為 `push` 方法會改變陣列的值) +moreNumbers.length = 3; // 錯誤,`length` 為唯讀 +numbers = moreNumbers; // 錯誤,修改陣列的方法並不存在於唯讀陣列 + +// 可以使用聯合型別(union type)來定義不同的資料型別 +type State = + | { type: "loading" } + | { type: "success", value: number } + | { type: "error", message: string }; + +declare const state: State; +if (state.type === "success") { + console.log(state.value); +} else if (state.type === "error") { + console.error(state.message); +} + +// 樣板實字(template literal)型別 +// 可以用來建立複雜的字串型別 +type OrderSize = "regular" | "large"; +type OrderItem = "Espresso" | "Cappuccino"; +type Order = `A ${OrderSize} ${OrderItem}`; + +let order1: Order = "A regular Cappuccino"; +let order2: Order = "A large Espresso"; +let order3: Order = "A small Espresso"; // 錯誤 + +// 迭代器(iterator)與產生器(generator) + +// for..of 陳述式 +// 循覽物件的每個成員「值」(value) +let arrayOfAnyType = [1, "string", false]; +for (const val of arrayOfAnyType) { + console.log(val); // 1, "string", false +} + +let list = [4, 5, 6]; +for (const i of list) { + console.log(i); // 4, 5, 6 +} + +// for..in 陳述式 +// 循覽物件的每個成員的「鍵」(key) +for (const i in list) { + console.log(i); // 0, 1, 2 +} + +// 型別斷言(assertion) +let foo = {} // 建立一個名為 `foo` 的空物件 +foo.bar = 123 // 錯誤,`bar` 屬性並不存在於 `{}` +foo.baz = 'hello world' // 錯誤:`baz` 屬性並不存在於 `{}` + +// 因為 `foo` 的推斷型別是 `{}`(一個無任何屬性的物件),所以不允許新增 `bar`、`baz` 及其他任何名稱的屬性。然而,通過型別斷言,以下程式碼將能夠通過 TS 的檢查: +interface Foo { + bar: number; + baz: string; +} + +let foo = {} as Foo; // 這裡使用型別斷言 +foo.bar = 123; +foo.baz = 'hello world' +``` + +## 延伸閱讀 + +* [TypeScript官網](https://www.typescriptlang.org/) +* [TypeScript原始碼](https://github.com/microsoft/TypeScript) +* [Learn TypeScript](https://learntypescript.dev/)