# Union Types และ Intersection Types

มีบ่อยครั้งที่ Type ที่สร้างขึ้นมาจะไม่ครอบคลุมการใช้งานได้ทั้งหมด หรือมีความต้องการที่จะใช้ตัวแปรเก็บค่าที่มีความซับซ้อนมากขึ้น เราสามารถใช้ Union Types และ Intersection Types มาช่วยได้

### Union Types

สำหรับการ Union เคยเกริ่นไปแล้วในบทก่อน ([Literal Types](https://www.monosor.dev/courses/typescript-101/literal-types)) คือการใช้ `|` เพื่อเชื่อม Type เข้าด้วยกัน ทำให้ Type ที่สร้างขึ้นใหม่มีความยืดหยุ่นมากขึ้น

```typescript
type ID = string | number // ID สามารถเป็น string `หรือ` ตัวเลขก็ได้

const a: ID = "abc"
const b: ID = 101
```

เราจะนำ Union ไปเขียนเป็น Inline ในฟังก์ชั่นก็ได้เช่นกัน

```typescript
function sayHello(people: string | string[]) { // รับ string หรือชุดอาเรย์ของ string
  ...
}
```

เมื่อใช้ Union แล้วต้องการใช้ตัวแปรนั้น จะต้องทำการเช็ค Type ก่อนในโค้ด เรียกว่าการ Narrowing เช่นตามตัวอย่าง `people` อาจเป็น String หรือ Array ก็ได้ ถ้าไม่เช็คแล้วใช้ Function กับตัวแปรนั้น อาจมี Error เกิดขึ้นได้

```typescript
function sayHello(people: string | string[]) {
  console.log(`สวัสดี ${people.join(", ")}`)
}

sayHello(["เจน", "นุุ่น"]) // ✅ สวัสดี เจน, นุ่น
sayHello("โบว์")         // ❌ Error
```

![ไม่สามารถใช้ join() ได้ เพราะถ้า people เป็น String จะเกิด Runtime error](https://912784263-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MMMgc-z4G6riCzlCyo9%2F-MNEKawmu_nPuEJQZp1Z%2F-MNEPle4K7vDE9pXUjE-%2Fimage.png?alt=media\&token=d154f7dd-10ff-4596-a28d-f7618222240c)

จากตัวอย่างข้างต้น ต้องทำการแก้ไขโค้ดให้รองรับทุก Type ที่ Union กัน เช่น

```typescript
function sayHello(people: string | string[]) {
  if (typeof people == "string") {
    console.log(`สวัสดี ${people} มาคนเดียวเหรอ`)
  } else {
    console.log(`สวัสดี ${people.join(", ")}`)
  }
}

sayHello(["เจน", "นุุ่น"]) // ✅ สวัสดี เจน, นุ่น
sayHello("โบว์")         // ✅ สวัสดี โบว์ มาคนเดียวเหรอ
```

อีกตัวอย่างหนึ่งที่จะได้ใช้ Union Type เช่น Literal Types

```typescript
type Color = "red" | "green" | "blue"

function printColorCode(c: Color): string {
  switch (c) {
    case "red":
      return "#FF0000"
    case "green":
      return "#00FF00"
    case "blue":
      return "#0000FF"
}
```

ถ้าในฟังก์ชั่นนี้ไม่ได้เช็คทุกกรณี เช่นมี case `red` กับ `green` แต่ไม่มี `blue` หรือสะกดเคสใดๆ ผิด จะเกิด Error ขึ้น

![Error เพราะพิมพ์ "blue" ผิด](https://912784263-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MMMgc-z4G6riCzlCyo9%2F-MNEVzVcT10-cvVgqEUX%2F-MNEW-6z4aGq54sdTIsm%2Fimage.png?alt=media\&token=54d36077-4b33-44fc-8cbe-a6e105ffe6a2)

จะเห็นได้ว่า TypeScript จะทำให้เราเขียนโค้ดที่ Bug-free มากขึ้น เนื่องจากเรารองรับ Edge Case ท้ังหมดได้จากการจำกัด Type ที่จะใช้ในฟังก์ชั่น แต่ก็ยังมีความยืดหยุ่นจากการ Union ได้ ทำให้ไม่ต้องเขียนแยกกัน ทำให้มีโค้ดมากเกินความจำเป็น

### Intersection Types

การใช้ Intersection จะใช้กับ Interface จะเป็นเสมือนการ "ต่อ" Interface ต่างๆ เข้าด้วยกัน แต่เงื่อนไขจะแตกต่างกับ Union คือ Intersection นั้นค่าจะต้องเป็นออบเจกต์ที่มีหน้าตา ตรงกันกับทุก Type ที่นำมา Intersect กัน&#x20;

สมมุติว่ามี Interface ที่เป็นรูปวงกลม กับสี่เหลี่ยมที่มีค่าสี และคำนวณพื้นที่ได้

```typescript
interface ColorfulCircle {
  color: string
  radius: number
  area: () => number
}

interface ColorfulSquare {
  color: string
  size: number
  area: () => number
}
```

สังเกตว่ามีการใช้ชื่อและ Type ของ `color` และ `area` ซำ้กัน ซึ่งถ้าเราใช้ความสามารถของ Intersection (ตัว `&`) เราจะแยก Interface ที่ใช้ซำ้กันออกมา แล้ว Intersect ทีหลังได้ แบบนี้

```typescript
interface Colorful {
  color: string
}

interface Circle {
  radius: number
}

interface Square {
  size: number
}

interface Measurable {
  area: () => number
}

// มี color, radius และฟังก์ชั่น area
type ColorfulCircle = Colorful & Circle & Measurable

// มี color, size และฟังก์ชั่น area
type ColorfulSquare = Colorful & Square & Measurable
```

การใช้ Intersection จะทำให้เราสร้าง Interface เล็กๆ แล้วเอามา Reuse ต่อได้ ทำให้ Code อ่านง่ายขึ้นด้วย
