# TypeScript 幼幼班直播筆記(下)

主講:Hello
直播連結:https://youtu.be/Nn9cBo_4FUU
Playground:https://www.typescriptlang.org/play
## Type checking
### 型別檢測 Type Guard
型別檢測是在執行時可以限縮型別的表達式,通常會在條件式(例如if...else 語句)中進行檢查後。下面是常見的幾種方式:
1. 使用 JS 運算子:typeof 、 instanceof 或 isArray 等
2. 使用使用者自定義的型別檢查:型別謂語(type predicates)、in 關鍵字
> [TypeScript 資料型別 - 複合型別(Union & Intersection) & 型別檢測(Type Guard)](https://ithelp.ithome.com.tw/articles/10222470)
### 動態型別 Dynamic 與 靜態型別 Static
動態和靜態型別是針對 **型別檢查的時機** 來區分:
* **靜態型別**:在創造階段檢查型別 (TypeScript)
* **動態型別**:在執行階段檢查型別 (JavaScript)
### 強型別 與 弱型別
強弱之別是相對的,沒有絕對之分野,端看語言對型態檢查的嚴格程度、型態轉換規則是否多元。
在偏強型別語言,多數情況下型別轉換必須明確定義,但也有自動轉型的案例存在。
而偏弱型別語言,比如 JavaScript 則可自動進行型別轉換:
```javascript=
console.log(1 + '1') // 11
console.log(true + false) // 1
```
### Nominal Typing 和 Structural Typing
:::info
<b>Nominal Typing</b> 和 <b>Structural Typing</b> 是 C#/Java 語言中的概念
:::
**Nominal Typing** 會依據定義的「名稱」進行比對,假設今天有兩個物件,分別有不同名稱和同樣的結構,在 Nominal Typing 規則下進行比對會得到不相同的結果。
```c=
class Foo {
method(input: string): number { ... }
}
class Bar {
method(input: string): number { ... }
}
let foo: Foo = new Bar(); // Error
```
**Structural Typing** 則是根據定義的「結構」進行比對,例如 `OCaml`、`Haskell`、`Elm` 語言,只要結構相同,就會被判斷成相同物件。
```haskell=
class Foo {
method(input: string): number { ... }
}
class Bar {
method(input: string): number { ... }
}
let foo: Foo = new Bar(); // Okay
```
### Duck Typing
> Duck Typing 在程式設計中是動態型別的一種風格。
> 在這種風格中,一個物件有效的語意,不是由繼承自特定的類或實現特定的介面,而是由 **當前方法和屬性的集合** 決定。[wiki](https://zh.wikipedia.org/wiki/%E9%B8%AD%E5%AD%90%E7%B1%BB%E5%9E%8B)
<br>
## 複合型別 Union & Intersection
複合型別又可分為 `聯合型別 Union Type` 和 `交集型別 Intersection Type`。
### 聯合型別 Union Type
在上半部筆記也有提到 [聯合型別](/chJ7jxHXTMSpUdKLrcY4HQ?view#聯合型別-Union-Type),JavaScript 中經常會使用到,用 `|` 來表示資料的其中一種,在 TypeScript 中也用來限制宣告的型別。
```typescript=
let str: string | number
str = 'a' // Pass
str = 1 // Pass
str = { key: 'value' } // Error
```
:::info
聯合型別的變數在被賦值的時候,會根據型別推論的規則推斷出一個型別
:::
在以下範例中,變數 `str` 在第一次被賦值時被推論成字串,而在第二次賦值推論成數字,因此不能使用 `length` 取出長度。
```typescript=
let str: string | number
str = 'Hello'
console.log(str.length) // 5
str = 5
console.log(str.length)
// Property 'length' does not exist on type 'number'.
```
如果今天自定義兩個物件型別 `info1` 和 `info2`,裡面分別包含不同屬性,還有另外一個 `info3` 為前面兩者的聯合型別。
```typescript=
type info1 = {
name: string,
age: number
}
type info2 = {
name: string,
isChild: boolean
}
type info3 = info1 | info2
```
當宣告的的變數 **「完全符合」** 其中一種型別即可,若是只有部分滿足則還是會報錯。
```typescript=
let personA: info3 = {
name: 'Abby',
age: 23
} // Pass
let personB: info3 = {
name: 'John',
isChild: false
} // Pass
let personC: info3 = {
name: 'Mask'
} // Error
```
### 交集型別 Intersection Type
交集型別主要用來把多個型別合併為一個型別,並用 `&` 合併。
把上面範例修改成交集型別後,這時表示新的物件必須 **「同時包含」** `info1` 和 `info2` 的所有屬性。
```typescript=
type info1 = {
name: string,
age: number
}
type info2 = {
name: string,
isChild: boolean
}
type info3 = info1 & info2
```
```typescript=
let personA: info3 = {
name: 'Abby',
age: 23
} // Error
let personB: info3 = {
name: 'John',
isChild: false
} // Error
let personC: info3 = {
name: 'Mask',
age: 18,
isChild: false
} // Pass
```
<br>
## 介面 Interface 和 型別別名 Type Alias
[TypeScript 介面(Interface) v.s. 型別別名(Type Alias)](https://ithelp.ithome.com.tw/articles/10224646)
<br>
## 函式 Function
函式在 JavaScript 中代表一段可以被執行的物件,而在 TypeScript 中則代表可被執行的型別。
可以使用 Interface 和 Type Alias 來定義函示:
```typescript=
interface TwoNumberCalculation {
(x: number, y: number): number;
}
type TwoNumberCalc = (x: number, y: number) => number;
const add: TwoNumberCalculation = (a, b) => a + b;
const subtract: TwoNumberCalc = (x, y) => x - y;
```
### Function overloads
> 函式多載是 Ada、C++、C#、D和Java 等程式語言中具有的一項特性,這項特性允許建立數項名稱相同但輸入輸出類型或個數不同的子程式,它可以簡單地稱為一個單獨功能可以執行多項任務的能力。[wiki](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD)
<br>
## 類別 Class
Class 是 ES6 的語法,可看成 JavaScript 原型繼承 (prototype-based) 的語法糖,提供一個更簡潔的語法來建立物件和處理繼承。
* [MDN - Classes](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes)
* [TypeScript 資料型別 - 類別(Class)](https://ithelp.ithome.com.tw/articles/10224976)
### Class 宣告
Class 由三元素組成:建構函式 (constructor)、屬性、方法。
以下範例程式碼中,首先宣告一個 class 物件,裡面包含需要用到的屬性 `color`、`size`,建構式 constructor 將新的屬性賦予給新物件,最後透過 `getProperty` 方法回傳要顯示的內容。
定義完 dog 類別後,使用 `new dog()` 的方式宣告一個新物件 blackDog。
```typescript=
class dog {
color: string;
size: string;
constructor(color: string, size: string) {
this.color = color
this.size = size
}
getProperty(): string{
return `這是一隻${this.color}色的${this.size}狗`
}
}
const blackDog = new dog('黑', '大')
console.log(blackDog.getProperty())
// 這是一隻黑色的大狗
```
### Extends
Class 也能使用 extends 來繼承/擴展子類別,但要特別注意的是,如果原本的 class 已經包含一個建構函示,在使用 this 之前就必須先用 `super()` 來繼承原本的屬性。
而在 `getProperty()` 方法內,需要先用 `super.getProperty()` 來呼叫繼承父類別 dog 的方法。
```typescript=
class bark extends dog {
isBark: boolean;
constructor(color: string, size: string, isBark: boolean) {
super(color, size);
this.isBark = isBark;
}
getProperty(): string {
if (this.isBark) {
return `${super.getProperty()},而且會吠`;
} else {
return `${super.getProperty()},但不會吠`;
}
}
}
const whiteDog = new bark('白', '小', true)
console.log(whiteDog.getProperty())
// 這是一隻白色的小狗,而且會吠
```
### 存取修飾符 Access modifier
存取修飾符用來限制類別中的屬性或方法,在類別外部被呼叫的權限,目前分為三種:
* **public** - 完全公開,可以在任何地方使用 (預設)
* **private** - 只能在本身類別使用
* **protected** - 只能在本身類別內以及繼承的子類別中使用
```typescript=
class Foo {
constructor(x: number, y: number, z: number) {
this.x = x
this.y = y
this.z = z
}
// 修飾符會標註在類別內部屬性或方法名稱的前面
public x: number
private y: number
protected z: number
}
```
#### 類別實例化,宣告新物件
```typescript=
const foo = new Foo(1, 2, 3)
foo.x = 4
foo.y = 5 // Error: Property 'y' is private
foo.z = 6 // Error: Property 'z' is protected
```
#### 繼承子類別
```typescript=
class FooChild extends Foo {
constructor(x: number, y: number, z: number) {
super(x, y, z)
this.x = 0
this.y = 0 // Error: Property 'y' is private
this.z = 0
}
}
```
### 簡化類別 Param properties
* 簡化前
```typescript=
class Foo {
constructor(x: number, y: number, z: number) {
this.x = x
this.y = y
this.z = z
}
public x: number
private y: number
protected z: number
}
```
* 簡化後
```typescript=
class Foo {
constructor(
public x: number
private y: number
protected z: number
) {}
}
```
---
* [TypeScript 幼幼班直播筆記(上)](/chJ7jxHXTMSpUdKLrcY4HQ)
## 參考資料
* [Type Systems: Structural vs. Nominal typing explained](https://medium.com/@thejameskyle/type-systems-refinements-explained-26f713c6cc2a)
* [Typescript 初心者手札](https://ithelp.ithome.com.tw/users/20120053/ironman/2273)