1. If-Else Repetition πŸ‘©β€πŸ’»

1. Swift

func beverage(_ num: Int) -> String {
  if (num == 0) {
    return "Juice";
  } else if (num == 1) {
    return "Soda";
  } else if (num == 2) {
    return "Beer";
  } else if (num == 3) {
    return "Wine";
  } else if (num == 4) {
    return "Tequila";
  } else {
    return "Water";
  }
}

print(beverage(0)) // Juice
print(beverage(3)) // Wine
print(beverage(5)) // Water

2. TypeScript

const beverage = (num: number): string => {
  if (num === 0) {
    return 'Juice';
  } else if (num === 1) {
    return 'Soda';
  } else if (num === 2) {
    return 'Beer';
  } else if (num === 3) {
    return 'Wine';
  } else if (num === 4) {
    return 'Tequila';
  } else {
    return 'Water';
  }
};

console.log(beverage(0))  // Juice
console.log(beverage(3))  // Wine
console.log(beverage(5))  // Water

일을 ν•˜λ©΄μ„œ if-else if-else if-else if...-else μ΄λŸ°μ‹μœΌλ‘œ 50개의 쑰건을 μ—°κ²°ν•œ μ½”λ“œλ₯Ό λ³Έ 적이 μžˆλ‹€. 😱

μ΄λŸ°μ‹μ˜ μ½”λ“œλŠ” 가독성도 λ‚˜μ˜μ§€λ§Œ 쑰건 검사λ₯Ό μ΅œμ•…μ˜ κ²½μš°μ— if, else if 의 개수만큼 κ²€μ‚¬ν•΄μ•Όν•˜λŠ” μ΅œμ•…μ˜ Control Flow μ•Œκ³ λ¦¬μ¦˜μ΄ νƒ„μƒν•˜κ²Œ λœλ‹€. κ°œμΈμ μœΌλ‘œλŠ” 쑰건이 3개일 κ²½μš°λŠ” κ·Έλƒ₯ if-else if-else λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜, if & Ternary Operatorλ₯Ό μ‚¬μš©ν•˜κ³ , 4개 이상일 경우 switch λ˜λŠ” Key-Value Types λ₯Ό μ‚¬μš©ν•˜κ³€ ν•œλ‹€.


2. Switch-Case πŸ‘©β€πŸ’»

μœ„μ™€ 같은 문제λ₯Ό κ°€μž₯ μ‰½κ²Œ ν•΄κ²°ν•  수 μžˆλŠ” 방법은 Switch-Caseλ₯Ό μ‚¬μš©ν•˜λŠ” 것이닀.

1. Swift

func beverage(_ num: Int) -> String {
    switch num {
    case 0: return "Juice"
    case 1: return "Soda"
    case 2: return "Beer"
    case 3: return "Wine"
    case 4: return "Tequila"
    default: return "Water"
    }
}

print(beverage(0)) // Juice
print(beverage(3)) // Wine
print(beverage(5)) // Water

μ•„κΉŒλ³΄λ‹€ 가독성이 훨씬 μ’‹μ•„μ‘Œμ„ 뿐 μ•„λ‹ˆλΌ, Hashable Key λ₯Ό μ΄μš©ν•΄ Jump Table 을 ν•˜λ―€λ‘œ μ„±λŠ₯ λ˜ν•œ μ’‹μ•„μ‘Œλ‹€.

2. TypeScript

const beverage = (num: number): string => {
  switch (num) {
    case 0:
      return 'Juice'
    case 1:
      return 'Soda'
    case 2:
      return 'Beer'
    case 3:
      return 'Wine'
    case 4:
      return 'Tequila'
    default:
      return 'Water'
  }
}

console.log(beverage(0))  // Juice
console.log(beverage(3))  // Wine
console.log(beverage(5))  // Water

개인적으둜 λŒ€λΆ€λΆ„μ˜ IDE λŠ” 물둠이고 Prettier κ°€ case 와 body λ₯Ό κ°•μ œ μ€„λ°”κΏˆ ν•˜λŠ” 것이 λ§ˆμŒμ— 듀지 μ•ŠλŠ”λ‹€. 였히렀 Dirty Code κ°€ λ˜λŠ” λŠλ‚Œμ΄λž„κΉŒ? κ·Έλž˜μ„œ Clean Code μ—μ„œ Key-Value Types λ₯Ό ꢌμž₯ν•˜λŠ” 것인가 싢기도 ν•˜λ‹€.

Caution

JavaScript language overview λ₯Ό 보면 λ‹€μŒκ³Ό 같이 λ‚˜μ˜¨λ‹€.

Similar to C, case clauses are conceptually the same as labels, so if you don’t add a break statement, execution will β€œfall through” to the next level. However, they are not actually jump tables β€” any expression can be part of the case clause, not just string or number literals, and they would be evaluated one-by-one until one equals the value being matched. Comparison takes place between the two using the === operator.

즉, JavaScript μ—μ„œ Switch λŠ” Hash Table 을 μ΄μš©ν•œ Jump λ₯Ό ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ„±λŠ₯상 이점은 μ—†λŠ” κ²ƒμœΌλ‘œ 보인닀.

λ§ˆμ§€λ§‰μœΌλ‘œ λ‹€μŒ μ„Ήμ…˜μ—μ„œ Switch-Case λ§ˆμ €λ„ μ œκ±°ν•΄λ³΄λ„λ‘ ν•˜μž.


3. Key-Value Types πŸ‘©β€πŸ’»

사싀 Switch-Case λ‚˜ Key-Value λͺ¨λ‘ Key 에 ν•΄λ‹Ήν•˜λŠ” 데이터가 Hashable 을 λ§Œμ‘±ν•΄μ•Όν•˜λ―€λ‘œ 큰 μ°¨μ΄λŠ” μ—†λ‹€.

ν•˜μ§€λ§Œ Swift μ—μ„œλŠ” Switch-Case 의 경우 쑰건을 λͺ¨λ‘ μ»€λ²„ν•˜μ§€ λͺ»ν–ˆμ„ 경우 compiler κ°€ μ—λŸ¬λ₯Ό λ„μ›Œμ£ΌλŠ”λ°, λ§Œμ•½ compile-time 에 확인이 λΆˆκ°€λŠ₯ν•œ κ²½μš°κ°€ μžˆμ„ 경우(그런 κ²½μš°κ°€ μžˆμ„ 수 μžˆμ„κΉŒβ€¦? πŸ™„) Runtime Error 둜 μ΄μ–΄μ§ˆ 수 μžˆλ‹€.

그리고 개인적으둜 Swift 의 Switch-Case λŠ” λ‚˜λ¦„ 가독성 μ’‹λ‹€κ³  μƒκ°ν•˜λŠ”λ°, TypeScript 의 κ²½μš°λŠ” μ•„λ‹ˆκΈ°λ„ ν•˜κ³ , 특히 TypeScript λŠ” Enumeration 의 경우 기쑴의 enum은 JavaScript 둜 transpile 될 λ•Œ IIFE둜 λ³€ν™˜λ˜μ–΄ Bundler 의 Tree-shaking 이 μž‘λ™ν•˜μ§€ μ•Šμ•„ λΆˆν•„μš”ν•œ μ½”λ“œκ°€ μ œκ±°λ˜μ§€ μ•ŠλŠ” λ¬Έμ œκ°€ μžˆλ‹€.
이둜 인해 as constλ₯Ό μ‚¬μš©ν•œ Enumeration 선언이 ꢌμž₯λ˜λŠ”λ°, 이걸 μ‚¬μš©ν•  경우 μ–΄μ°¨ν”Ό Object 이기 λ•Œλ¬Έμ— Switch-Case λ₯Ό μ‚¬μš©ν•  ν•„μš”κ°€ μ—†μ–΄μ Έ 버린닀(TypeScript 의 Switch-Case Syntax κ°€ 영 κΉ¨λ—ν•˜μ§€ λͺ»ν•œ 것도 μžˆκ³ β€¦ 🀭).

μ•„λ¬΄νŠΌ Clean Code μ—μ„œ λŒ€λΆ€λΆ„ Switch λ§ˆμ €λ„ Key-Value Types 둜 λ°”κΎΈλŠ” 것을 ꢌμž₯ν•œλ‹€. λ”°λΌμ„œ μš°λ¦¬λ„ Switch-Case λ₯Ό Key-Value Types λ₯Ό μ‚¬μš©ν•œ μ½”λ“œλ‘œ λ°”κΏ”λ³΄μž.

1. Swift

func beverage(_ num: Int) -> String {
    let switcher: [Int: String] = [
        0: "Juice",
        1: "Soda",
        2: "Beer",
        3: "Wine",
        4: "Tequila"
    ]
    return switcher[num] ?? "Water"
}

[Int, String] Type 의 Dictionary λ₯Ό μ΄μš©ν–ˆλ‹€. λ§Œμ•½ 이 Dictionary λ₯Ό λ°–μœΌλ‘œ λΉΌλ‚Ό 경우 ν•¨μˆ˜λŠ” 더 간단해진닀.

let switcher: [Int: String] = [
    0: "Juice",
    1: "Soda",
    2: "Beer",
    3: "Wine",
    4: "Tequila"
]

func beverage(_ num: Int) -> String {
    switcher[num] ?? "Water"
}

그런데 μ΄λ ‡κ²Œ ν•˜λ©΄ λΆˆν•„μš”ν•œ λ³€μˆ˜ μ˜€μ—Όμ΄ λ°œμƒν•œλ‹€. λ§Œμ•½ μž¬μ‚¬μš©κΉŒμ§€ κ³ λ €ν•΄ Switch-Caseλ₯Ό λŒ€μ‹ ν•  λ‘œμ§μ„ 별도 Utilities 둜 λ§Œλ“€ 경우 λ‹€μŒκ³Ό 같이 Currying μ‹œν‚¬ 수 μžˆλ‹€.

func switchCase<Key: Hashable, Value>(switcher: [Key: Value], defaultValue: Value) -> (_ key: Key) -> Value {
    { key in switcher[key] ?? defaultValue }
}

이제 이 Curry Function 을 μ΄μš©ν•΄ λ‹€μ‹œ beverage(_:)λ₯Ό μ •μ˜ν•΄λ³΄μž.

let beverage = switchCase(switcher: [0: "Juice",
                                     1: "Soda",
                                     2: "Beer",
                                     3: "Wine",
                                     4: "Tequila"],
                          defaultValue: "Water")

print(beverage(0)) // Juice
print(beverage(3)) // Wine
print(beverage(5)) // Water

2. TypeScript

TypeScript μ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” Key-Value Types λŠ” Object, Map, Record 등이 μžˆλ‹€. Record μ•Ό μ–΄μ°¨ν”Ό Object 둜 transpile 될거고, Object λŠ” Key κ°€ κ°€μ§ˆ 수 μžˆλŠ” Types κ°€ String 뿐이닀. TypeScript μ½”λ“œ μƒμ—μ„œμ•Ό Record λ₯Ό μ΄μš©ν•΄ Key 에 λ‹€λ₯Έ Types λ₯Ό μ •μ˜ν•  수 μžˆμ§€λ§Œ JavaScript μ½”λ“œμ—μ„œλŠ” μ‚¬μš©ν•  수 μ—†λŠ” 방법이닀.

반면 Map을 μ‚¬μš©ν•  경우 Switch-Case 의 default case λ₯Ό μœ νš¨ν•˜μ§€ μ•Šμ€ Key 둜 μΈμ‹ν•˜λ―€λ‘œ Recordλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 더 μ’‹λ‹€. λ”°λΌμ„œ TypeScript 버전은 Recordλ₯Ό μ‚¬μš©ν•˜κ³ , JavaScript 버전은 Map을 μ‚¬μš©ν•΄ 각각 κ΅¬ν˜„ν•΄λ³΄μž.

const beverage = (num: number): string => {
  const switcher: Record<number, string> = {
    0: "Juice",
    1: "Soda",
    2: "Beer",
    3: "Wine",
    4: "Tequila",
  }
  return switcher[num] ?? "Water"
}

Record<Int, String> Type 을 μ‚¬μš©ν–ˆλ‹€. 이 Record λ₯Ό λ°–μœΌλ‘œ λΉΌλ‚΄λ³΄μž.

const switcher: Record<number, string> = {
    0: "Juice",
    1: "Soda",
    2: "Beer",
    3: "Wine",
    4: "Tequila",
}

const beverage = (num: number): string => switcher[num] ?? "Water"

λ§ˆμ°¬κ°€μ§€λ‘œ Currying μ‹œμΌœλ³΄μž.

type Hashable = string | number
const switchCase = <Key extends Hashable, Value, Switcher extends Record<Key, Value>>(switcher: Switcher, defaultValue: Value) => (key: Key) => (
    switcher[key] ?? defaultValue
)

이제 이 Curry Function 을 μ΄μš©ν•΄ λ‹€μ‹œ beverage(_:)λ₯Ό μ •μ˜ν•΄λ³΄μž.

const beverage = switchCase({
        0: "Juice",
        1: "Soda",
        2: "Beer",
        3: "Wine",
        4: "Tequila"
    },
    "Water")

console.log(beverage(0))  // Juice
console.log(beverage(3))  // Wine
console.log(beverage(5))  // Wate

3. JavaScript

const beverage = (num) => {
  const switcher = new Map([
    [0, "Juice"],
    [1, "Soda"],
    [2, "Beer"],
    [3, "Wine"],
    [4, "Tequila"]
  ])
  return switcher.get(num) ?? "Water"
}

Map<Int, String> Type 을 μ‚¬μš©ν–ˆλ‹€. 이 Map 을 λ°–μœΌλ‘œ λΉΌλ‚΄λ³΄μž.

const switcher = new Map([
  [0, "Juice"],
  [1, "Soda"],
  [2, "Beer"],
  [3, "Wine"],
  [4, "Tequila"]
])

const beverage = (num) => switcher.get(num) ?? "Water"

λ§ˆμ°¬κ°€μ§€λ‘œ Currying μ‹œμΌœλ³΄μž.

const switchCase = (switcher, defaultValue) => (key) => switcher[key] ?? defaultValue

이제 이 Curry Function 을 μ΄μš©ν•΄ λ‹€μ‹œ beverage(_:)λ₯Ό μ •μ˜ν•΄λ³΄μž.

const beverage = switchCase(new Map([
      [0, "Juice"],
      [1, "Soda"],
      [2, "Beer"],
      [3, "Wine"],
      [4, "Tequila"]
    ]),
    "Water")

console.log(beverage(0))  // Juice
console.log(beverage(3))  // Wine
console.log(beverage(5))  // Water

4. Enumeration πŸ‘©β€πŸ’»

Switch-Case κ°€ μœ μš©ν•˜κ²Œ μ‚¬μš©λ˜λŠ” κ³³ 쀑 ν•˜λ‚˜κ°€ Enumeration 을 Control Flow 처리 ν•  λ•Œλ‹€. Enumeration 의 Control Flow μ—­μ‹œ Switch-Case λŒ€μ‹  Key-Value λ₯Ό μ μš©ν•΄λ³΄μž.

1. Swift

enum CompassPoint: String {
    case north, south, east, west
}


  • Switch-Case
func directionToHead(_ direction: CompassPoint) -> String {
    switch direction {
    case .north: return "Lots of planets have a north"
    case .south: return "Watch out for penguins"
    case .east: return "Where the sun rises"
    case .west: return "Where the skies are blue"
    }
}

print(directionToHead(.north))  // Lots of planets have a north
  • Key-Value
let compassSwithcer: [CompassPoint: String] = [
    .north: "Lots of planets have a north",
    .south: "Watch out for penguins",
    .east: "Where the sun rises",
    .west: "Where the skies are blue"
]

func directionToHead(_ direction: CompassPoint) -> String {
    compassSwithcer[direction]!
}

print(directionToHead(.east))   // Where the sun rises
  • Curry Function

λ§ˆμ°¬κ°€μ§€λ‘œ Curry Function 을 μ μš©ν•΄λ³΄μž.

let directionToHead = switchCase(switcher: [CompassPoint.north: "Lots of planets have a north",
                                            CompassPoint.south: "Watch out for penguins",
                                            CompassPoint.east: "Where the sun rises",
                                            CompassPoint.west: "Where the skies are blue"],
                                 defaultValue: "Water")

print(directionToHead(.east))   // Where the sun rises

2. TypeScript

  • Switch-Case

TypeScript 의 Old Syntax μ—μ„œ μ‚¬μš©λ˜λŠ” enum을 μ‚¬μš©ν•  κ²½μš°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

enum CompassPoint {
  North = "Lots of planets have a north",
  South = "Watch out for penguins",
  East = "Where the sun rises",
  West = "Where the skies are blue"
}
const directionToHead = (direction: CompassPoint): string => {
  switch (direction) {
    case CompassPoint.North: return "Lots of planets have a north"
    case CompassPoint.South: return "Watch out for penguins"
    case CompassPoint.East: return "Where the sun rises"
    case CompassPoint.West: return "Where the skies are blue"
  }
}

console.log(directionToHead(CompassPoint.North)) // Lots of planets have a north

ν•˜μ§€λ§Œ Key-Value Types μ—μ„œ μ„€λͺ…ν–ˆλ“―이 IIFE둜 λ³€ν™˜λœ μ½”λ“œμ˜ Tree-shaking λ―Έμž‘λ™ 문제둜 Modern TypeScript μ—μ„œλŠ” const Object as constκ°€ ꢌμž₯λœλ‹€.

const CompassPoint = {
  North: "Lots of planets have a north",
  South: "Watch out for penguins",
  East: "Where the sun rises",
  West: "Where the skies are blue"
} as const

type CompassPointKeys = keyof typeof CompassPoint
const directionToHead = (direction: CompassPointKeys): string => {
  switch (direction) {
    case "North": return "Lots of planets have a north"
    case "South": return "Watch out for penguins"
    case "East": return "Where the sun rises"
    case "West": return "Where the skies are blue"
  }
}

console.log(directionToHead("North")) // Lots of planets have a north
  • Key-Value
const directionToHead = (direction: CompassPointKeys): string => CompassPoint[direction]

console.log(directionToHead("East"))  // Where the sun rises

const Object as constλ₯Ό Enumeration 으둜 μ‚¬μš©ν•  경우 이미 Object κ°€ μ •μ˜λ˜μ–΄ 있기 λ•Œλ¬Έμ— λ³„λ„λ‘œ μ •μ˜ν•  ν•„μš”κ°€ μ—†λ‹€. 이 경우 였히렀 Switch-Case λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 λΆˆν•„μš”ν•œ μ½”λ“œλ₯Ό μƒμ„±ν•˜κ³  λΆˆν•„μš”ν•œ λ‘œμ§μ„ μΆ”κ°€ν•˜λŠ” μ…ˆμ΄ λœλ‹€.
λ§ˆμ°¬κ°€μ§€λ‘œ Key-Value λ₯Ό λ”°λ‘œ μ •μ˜ν•  ν•„μš”κ°€ μ—†μœΌλ―€λ‘œ Curry Function μ—­μ‹œ ν•„μš”κ°€ μ—†λ‹€.




Reference

  1. β€œJavaScript language overview.” MDN, May. 04, 2023, JavaScript language overview.