2024-05-15.md

🏑

DIL: μ΄νŽ™ν‹°λΈŒ νƒ€μž…μŠ€ν¬λ¦½νŠΈ

μŠ€ν„°λ””: μ›”κ°„ CS, https://github.com/monthly-cs/2024-05-effective-typescript
μž‘μ„±μΌ: 2024-05-15
μž‘μ„±μž: dusunax


μ•„μ΄ν…œ 7: νƒ€μž…μ΄ κ°’λ“€μ˜ 집합이라고 μƒκ°ν•˜κΈ° Think of Types as Sets of Values

  • λŸ°νƒ€μž„μ— λͺ¨λ“  λ³€μˆ˜λŠ” κ³ μœ ν•œ 값을 가짐
  • tsκ°€ 였λ₯˜ 체크λ₯Ό ν•˜λŠ” μˆœκ°„μ—λŠ” => 값이 μ•„λ‹Œ νƒ€μž…μ„ 가지고 있음
    • 즉, νƒ€μž… = ν• λ‹Ή κ°€λŠ₯ν•œ κ°’λ“€μ˜ 집합 sets of values
    • 이 집합은? νƒ€μž…μ˜ λ²”μœ„

집합 set

  • number νƒ€μž…μ€ Number ν˜Ήμ€ strictNullCheck 섀정에 따라 null, undefined을 ν¬ν•¨ν•œλ‹€
  • κ°€μž₯ μž‘μ€ 집합은 never. 아무 값도 ν¬ν•¨ν•˜μ§€ μ•ŠλŠ”λ‹€ (곡집합, empty set)
    const x: never = 12;
    //    ~ Type 'number' is not assignable to type 'never'.
    
  • ν•œ 가지 κ°’λ§Œ κ°€μ§€λŠ” unit, literal νƒ€μž…
    type A = "A";
    type B = "B";
    type Twelve = 12;
    
  • κ°’ 집합이 λ‘˜ 이상 μ‘°ν•©λœ union νƒ€μž… (합집합, union)
    type AB = "A" | "B";
    type AB12 = "A" | "B" | 12;
    

μ§‘ν•©μ˜ κ΄€μ μ—μ„œμ˜ νƒ€μž…

"ν• λ‹Ή κ°€λŠ₯ν•œ", assignable

  • ~의 μ›μ†Œ(κ°’κ³Ό νƒ€μž…) member
  • ~의 뢀뢄집합(νƒ€μž…κ³Ό νƒ€μž…) subset

subset

  • 집합 κ΄€μ μ—μ„œ νƒ€μž…μ²΄μ»€μ˜ 역할은 ν•˜λ‚˜μ˜ 집합이 λ‹€λ₯Έ μ§‘ν•©μ˜ λΆ€λΆ„ 집합인지 확인

    // OK, {"A", "B"} is a subset of {"A", "B"}:
    const ab: AB = Math.random() < 0.5 ? "A" : "B";
    const ab12: AB12 = ab; // OK, {"A", "B"} is a subset of {"A", "B", 12}
    
    declare let twelve: AB12;
    const back: AB = twelve;
    //    ~~~~ Type 'AB12' is not assignable to type 'AB'
    //           Type '12' is not assignable to type 'AB'
    
  • μ‹€μ œ λ‹€λ£¨κ²Œ λ˜λŠ” νƒ€μž…μ€ λŒ€λΆ€λΆ„ λ²”μœ„κ°€ λ¬΄ν•œλŒ€λ‹€.

    interface Identified {
      id: string;
    }
    // μ–΄λ–€ 객체가 string으둜 ν• λ‹Ή κ°€λŠ₯ν•œ id 속성을 가지고 μžˆλ‹€λ©΄? κ°μ²΄λŠ” Identifiedλ‹€. (ꡬ쑰적 타이핑)
    // excess property checking을 μƒκ°ν•˜λ‹€λ³΄λ©΄ κ°„κ³Όν•˜κΈ° 쉬움 (Item 11에 λ‚˜μ˜¬ 것)
    

μ§‘ν•©μ˜ μ—°μ‚°

  • & μ—°μ‚°μžλŠ” intersection, 두 νƒ€μž…μ˜ ꡐ집합을 κ³„μ‚°ν•œλ‹€
    interface Person {
      name: string;
    }
    interface Lifespan {
      birth: Date;
      death?: Date;
    }
    type PersonSpan = Person & Lifespan;
    // Personκ³Ό Lifespan μΈν„°νŽ˜μ΄μŠ€λŠ” κ³΅ν†΅μœΌλ‘œ κ°€μ§€λŠ” 속성이 μ—†λ‹€
    // νƒ€μž… μ—°μ‚°μžλŠ” μΈν„°νŽ˜μ΄μŠ€μ˜ 속성이 μ•„λ‹Œ, κ°’μ˜ 집합에 μ μš©λœλ‹€. (νƒ€μž…μ˜ λ²”μœ„μ— μ μš©λœλ‹€)
    // - Personκ³Ό Lifespan을 λ‘˜ λ‹€ κ°€μ§€λŠ” 값은 intersection에 μ†ν•˜κ²Œ λœλ‹€
    // - μ„Έ 가지보닀 더 λ§Žμ€ 속성을 κ°€μ§€λŠ” 값도 PersonSpan에 μ†ν•œλ‹€.
    
  • | μ—°μ‚°μžλŠ” 두 μΈν„°νŽ˜μ΄μŠ€μ˜ union

A와 B의 μ—°μ‚°

type A = { a: number; b: string };
type B = { b: string; c: boolean };

type ABInteraction = A & B;
type KeysOfABInteraction = keyof ABInteraction; // "a" | "b" | "c"

type ABUnion = A | B;
type KeysOfAUnion = keyof ABUnion; // "b"
// 집합과 νƒ€μž… μ—°μ‚°

keyof (A | B) = (keyof A) & (keyof B)
//  ꡐ집합에 λŒ€ν•œ keyof
//  A와 B의 곡톡 ν‚€λ§Œμ„ ν¬ν•¨ν•˜λŠ” μΈν„°μ„Ήμ…˜ νƒ€μž…
keyof (A & B) = (keyof A) | (keyof B)
//  합집합에 λŒ€ν•œ keyof
//  A와 B의 λͺ¨λ“  ν‚€λ₯Ό ν¬ν•¨ν•˜λŠ” μœ λ‹ˆμ–Έ νƒ€μž…

extends

interface Vector1D {
  x: number;
}
interface Vector2D extends Vector1D {
  // Vector1D의 μ„œλΈŒ νƒ€μž…μ΄λ‹€
  y: number;
}
interface Vector3D extends Vector2D {
  // Vector2D의 μ„œλΈŒ νƒ€μž…μ΄λ‹€
  z: number;
}
  • νƒ€μž…μ˜ μ§‘ν•©μ΄λΌλŠ” κ΄€μ μ—μ„œ extends의 μ˜λ―ΈλŠ” assignable(~에 ν• λ‹Ή κ°€λŠ₯ν•œ)κ³Ό λΉ„μŠ·ν•˜κ²Œ subset(~의 λΆ€λΆ„ 집합)μ΄λΌλŠ” 의미둜 받아듀일 수 μžˆλ‹€.
  • subtype μ„œλΈŒνƒ€μž…
    • μ–΄λ–€ 집합(set)이 λ‹€λ₯Έ 집합(set)의 λΆ€λΆ„ 집합(subset)μ΄λΌλŠ” 뜻
    • 클래슀 κ΄€μ μ—μ„œλŠ” μ„œλΈŒν΄λž˜μŠ€λ‹€.
    • μ§‘ν•©μ˜ κ΄€μ μ—μ„œλŠ”, 상속 관계가 μ•„λ‹ˆλΌ, λ²€ λ‹€μ΄μ–΄κ·Έλž¨μœΌλ‘œ κ·Έλ¦¬λŠ” 것이 적절 image

집합과 μ œλ„€λ¦­ νƒ€μž…, set & generic

  • μ œλ„€λ¦­ νƒ€μž…μ—μ„œ ν•œμ •μžλ‘œλ„ 쓰인닀. (~의 subset)
function getKey<K extends string>(val: any, key: K) {
  // 객체 μƒμ†μ˜ 관점이라면? 객체 Wrapper νƒ€μž… String의 μ„œλΈŒν΄λž˜μŠ€λ₯Ό μ •μ˜ν•˜λŠ” κ²ƒμ΄μ§€λ§Œ...
  // μ§‘ν•©μ˜ κ΄€μ μ—μ„œ μƒκ°ν•œλ‹€λ©΄? string의 λΆ€λΆ„ 집합 λ²”μœ„λ₯Ό κ°€μ§€λŠ” νƒ€μž…μ΄ λœλ‹€. K & string
  // ...
}
  • ν• λ‹Ήκ³Ό 상속 => 객체의 ν‚€ νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ” keyof T
function sortBy<K extends keyof T, T>(vals: T[], key: K): T[] {
  // ...
}
  • νƒ€μž…μ΄ μ—„κ²©ν•œ 상속 관계가 아닐 λ•ŒλŠ” 집합 μŠ€νƒ€μΌμ΄ λ°”λžŒμ§ν•˜λ‹€ image

  • λ°°μ—΄κ³Ό νŠœν”Œμ˜ 관계

    • 숫자 배열은 숫자의 μŒμ€ μ•„λ‹˜
    • number[]λ₯Ό [number, number]에 ν• λ‹Ήν•  수 μ—†μŒ
    • [number, number]λ₯Ό number[]에 ν• λ‹Ήν•  수 있음
const list = [1, 2];
//    ^? const list: number[]
const tuple: [number, number] = list;
//    ~~~~~ Type 'number[]' is not assignable to type '[number, number]'
//          Target requires 2 element(s) but source may have fewer
  • νƒ€μž…μ΄ κ°’μ˜ μ§‘ν•©μ΄λΌλŠ” 건? λ™μΌν•œ κ°’μ˜ 집합을 κ°€μ§€λŠ” 두 νƒ€μž…μ€ κ°™λ‹€
  • 의미적으둜 λ‹€λ₯΄κ³ , μš°μ—°νžˆ 같은 λ²”μœ„λ”λΌλ„ 같은 νƒ€μž…μ„ 두 번 μ •μ˜ν•  ν•„μš”λŠ” μ—†λ‹€

image

Things to Remember

  • Think of types as sets of values (the type's domain). These sets can either be finite (e.g., boolean or literal types) or infinite (e.g., number or string).
    • νƒ€μž…μ„ κ°’μ˜ μ§‘ν•©μœΌλ‘œ μ΄ν•΄ν•˜κΈ°. (νƒ€μž…μ˜ λ²”μœ„)
    • 집합은 μœ ν•œν•˜κ±°λ‚˜, λ¬΄ν•œν•˜λ‹€
  • TypeScript types form intersecting sets (a Venn diagram) rather than a strict hierarchy. Two types can overlap without either being a subtype of the other.
    • νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž…μ€ μ—„κ²©ν•œ 상속 관계가 μ•„λ‹ˆλΌ, κ²Ήμ³μ§€λŠ” μ§‘ν•©μœΌλ‘œ ν‘œν˜„λœλ‹€.
    • 두 νƒ€μž…μ€ μ„œλΈŒνƒ€μž…μ΄ μ•„λ‹ˆλ©΄μ„œλ„ 겹쳐질 수 μžˆλ‹€
  • Remember that an object can still belong to a type even if it has additional properties that were not mentioned in the type declaration.
    • 객체에 νƒ€μž… 선언에 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μΆ”κ°€ 속성이 μžˆλ”λΌλ„, κ·Έ νƒ€μž…μ— 속할 수 μžˆλ‹€
  • Type operations apply to a set's domain. The domain of A | B is the union of the domains of A and B.
    • νƒ€μž… 연산은 μ§‘ν•©μ˜ λ²”μœ„μ— μ μš©λœλ‹€.
      • A or BλŠ” Aλ²”μœ„μ™€ Bλ²”μœ„μ˜ 합집합이닀.
      • A and BλŠ” Aλ²”μœ„μ΄κ±°λ‚˜, Bλ²”μœ„μ΄λ‹€.
  • Think of "extends," "assignable to," and "subtype of" as synonyms for "subset of."
    • 상속, ν• λ‹Ήκ°€λŠ₯, μ„œλΈŒνƒ€μž…μ— 속함 == λΆ€λΆ„ 집합에 속함