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

Union Types

สำหรับการ Union เคยเกริ่นไปแล้วในบทก่อน (Literal Types) คือการใช้ | เพื่อเชื่อม Type เข้าด้วยกัน ทำให้ Type ที่สร้างขึ้นใหม่มีความยืดหยุ่นมากขึ้น
1
type ID = string | number // ID สามารถเป็น string `หรือ` ตัวเลขก็ได้
2
3
const a: ID = "abc"
4
const b: ID = 101
Copied!
เราจะนำ Union ไปเขียนเป็น Inline ในฟังก์ชั่นก็ได้เช่นกัน
1
function sayHello(people: string | string[]) { // รับ string หรือชุดอาเรย์ของ string
2
...
3
}
Copied!
เมื่อใช้ Union แล้วต้องการใช้ตัวแปรนั้น จะต้องทำการเช็ค Type ก่อนในโค้ด เรียกว่าการ Narrowing เช่นตามตัวอย่าง people อาจเป็น String หรือ Array ก็ได้ ถ้าไม่เช็คแล้วใช้ Function กับตัวแปรนั้น อาจมี Error เกิดขึ้นได้
1
function sayHello(people: string | string[]) {
2
console.log(`สวัสดี ${people.join(", ")}`)
3
}
4
5
sayHello(["เจน", "นุุ่น"]) // ✅ สวัสดี เจน, นุ่น
6
sayHello("โบว์") // ❌ Error
Copied!
ไม่สามารถใช้ join() ได้ เพราะถ้า people เป็น String จะเกิด Runtime error
จากตัวอย่างข้างต้น ต้องทำการแก้ไขโค้ดให้รองรับทุก Type ที่ Union กัน เช่น
1
function sayHello(people: string | string[]) {
2
if (typeof people == "string") {
3
console.log(`สวัสดี ${people} มาคนเดียวเหรอ`)
4
} else {
5
console.log(`สวัสดี ${people.join(", ")}`)
6
}
7
}
8
9
sayHello(["เจน", "นุุ่น"]) // ✅ สวัสดี เจน, นุ่น
10
sayHello("โบว์") // ✅ สวัสดี โบว์ มาคนเดียวเหรอ
Copied!
อีกตัวอย่างหนึ่งที่จะได้ใช้ Union Type เช่น Literal Types
1
type Color = "red" | "green" | "blue"
2
3
function printColorCode(c: Color): string {
4
switch (c) {
5
case "red":
6
return "#FF0000"
7
case "green":
8
return "#00FF00"
9
case "blue":
10
return "#0000FF"
11
}
Copied!
ถ้าในฟังก์ชั่นนี้ไม่ได้เช็คทุกกรณี เช่นมี case red กับ green แต่ไม่มี blue หรือสะกดเคสใดๆ ผิด จะเกิด Error ขึ้น
Error เพราะพิมพ์ "blue" ผิด
จะเห็นได้ว่า TypeScript จะทำให้เราเขียนโค้ดที่ Bug-free มากขึ้น เนื่องจากเรารองรับ Edge Case ท้ังหมดได้จากการจำกัด Type ที่จะใช้ในฟังก์ชั่น แต่ก็ยังมีความยืดหยุ่นจากการ Union ได้ ทำให้ไม่ต้องเขียนแยกกัน ทำให้มีโค้ดมากเกินความจำเป็น

Intersection Types

การใช้ Intersection จะใช้กับ Interface จะเป็นเสมือนการ "ต่อ" Interface ต่างๆ เข้าด้วยกัน แต่เงื่อนไขจะแตกต่างกับ Union คือ Intersection นั้นค่าจะต้องเป็นออบเจกต์ที่มีหน้าตา ตรงกันกับทุก Type ที่นำมา Intersect กัน
สมมุติว่ามี Interface ที่เป็นรูปวงกลม กับสี่เหลี่ยมที่มีค่าสี และคำนวณพื้นที่ได้
1
interface ColorfulCircle {
2
color: string
3
radius: number
4
area: () => number
5
}
6
7
interface ColorfulSquare {
8
color: string
9
size: number
10
area: () => number
11
}
Copied!
สังเกตว่ามีการใช้ชื่อและ Type ของ color และ area ซำ้กัน ซึ่งถ้าเราใช้ความสามารถของ Intersection (ตัว &) เราจะแยก Interface ที่ใช้ซำ้กันออกมา แล้ว Intersect ทีหลังได้ แบบนี้
1
interface Colorful {
2
color: string
3
}
4
5
interface Circle {
6
radius: number
7
}
8
9
interface Square {
10
size: number
11
}
12
13
interface Measurable {
14
area: () => number
15
}
16
17
// มี color, radius และฟังก์ชั่น area
18
type ColorfulCircle = Colorful & Circle & Measurable
19
20
// มี color, size และฟังก์ชั่น area
21
type ColorfulSquare = Colorful & Square & Measurable
Copied!
การใช้ Intersection จะทำให้เราสร้าง Interface เล็กๆ แล้วเอามา Reuse ต่อได้ ทำให้ Code อ่านง่ายขึ้นด้วย
Last modified 10mo ago