본문으둜 κ±΄λ„ˆλ›°κΈ°

🐀 Chapter 2: νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ νƒ€μž… μ‹œμŠ€ν…œ

πŸ₯• μ•„μ΄ν…œ 6. νŽΈμ§‘κΈ°λ₯Ό μ‚¬μš©ν•˜μ—¬ νƒ€μž… μ‹œμŠ€ν…œ νƒμƒ‰ν•˜κΈ°β€‹

  • νŽΈμ§‘κΈ°μ—μ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ–Έμ–΄ μ„œλΉ„μŠ€λ₯Ό 적극 ν™œμš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • νŽΈμ§‘κΈ°λ₯Ό μ‚¬μš©ν•˜λ©΄ μ–΄λ–»κ²Œ νƒ€μž… μ‹œμŠ€ν…œμ΄ λ™μž‘ν•˜λŠ”μ§€, 그리고 νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ–΄λ–»κ²Œ νƒ€μž…μ„ μΆ”λ‘ ν•˜λŠ”μ§€ κ°œλ…μ„ μž‘μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ λ™μž‘μ„ μ–΄λ–»κ²Œ λͺ¨λΈλ§ν•˜λŠ”지 μ•ŒκΈ° μœ„ν•΄ νƒ€μž… μ„ μ–Έ νŒŒμΌμ„ μ°Ύμ•„λ³΄λŠ” 방법을 터득해야 ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 7. νƒ€μž…μ΄ κ°’λ“€μ˜ 집합이라고 μƒκ°ν•˜κΈ°β€‹

λŸ°νƒ€μž„μ— λͺ¨λ“  λ³€μˆ˜λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ μ„Έμƒμ˜ κ°’μœΌλ‘œλΆ€ν„° μ •ν•΄μ§€λŠ” 각자의 κ³ μœ ν•œ 값을 κ°€μ§‘λ‹ˆλ‹€. λ³€μˆ˜μ—λŠ” λ‹€μŒμ²˜λŸΌ λ‹€μ–‘ν•œ μ’…λ₯˜μ˜ 값을 ν• λ‹Ήν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • 42
  • null
  • undefined
  • 'Canada'
  • { animal: 'Whale', weight_lbs: 40_000 }
  • /regex/
  • new HTMLButtonElement
  • (x, y) => x + y

κ·ΈλŸ¬λ‚˜ μ½”λ“œκ°€ μ‹€ν–‰λ˜κΈ° μ „, 즉 νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 였λ₯˜λ₯Ό μ²΄ν¬ν•˜λŠ” μˆœκ°„μ—λŠ” "νƒ€μž…"을 가지고 μžˆμŠ΅λ‹ˆλ‹€. "ν• λ‹Ή κ°€λŠ₯ν•œ κ°’λ“€μ˜ 집합"이 νƒ€μž…μ΄λΌκ³  μƒκ°ν•˜λ©΄ λ©λ‹ˆλ‹€. 이 집합은 νƒ€μž…μ˜ "λ²”μœ„"라고 λΆ€λ₯΄κΈ°λ„ ν•©λ‹ˆλ‹€.

κ°€μž₯ μž‘μ€ 집합은 아무 값도 ν¬ν•¨ν•˜μ§€ μ•ŠλŠ” 곡집합이며, νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” never νƒ€μž…μž…λ‹ˆλ‹€. never νƒ€μž…μœΌλ‘œ μ„ μ–Έλœ λ³€μˆ˜μ˜ λ²”μœ„λŠ” 곡집합이기 λ•Œλ¬Έμ— μ•„λ¬΄λŸ° 값도 ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

κ·Έλ‹€μŒμœΌλ‘œ μž‘μ€ 집합은 ν•œ 가지 κ°’λ§Œ ν¬ν•¨ν•˜λŠ” νƒ€μž…μž…λ‹ˆλ‹€. 이듀은 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ μœ λ‹›(unit) νƒ€μž…μ΄λΌκ³ λ„ λΆˆλ¦¬λŠ” λ¦¬ν„°λŸ΄ νƒ€μž…μž…λ‹ˆλ‹€.

type A = 'A';
type B = 'B';
type Twelve = 12;

두 개 ν˜Ήμ€ μ„Έ 개둜 묢으렀면 μœ λ‹ˆμ˜¨ νƒ€μž…μ„ μ‚¬μš©ν•©λ‹ˆλ‹€. μœ λ‹ˆμ˜¨ νƒ€μž…μ€ κ°’ μ§‘ν•©λ“€μ˜ 합집합을 μΌμ»«μŠ΅λ‹ˆλ‹€.

type AB = 'A' | 'B';
type AB12 = 'A' | 'B' | 12;

νƒ€μž…μ΄ μ§‘ν•©μ΄λΌλŠ” κ΄€μ μ—μ„œ extends의 μ˜λ―ΈλŠ” ~에 ν• λ‹Ή κ°€λŠ₯ν•œκ³Ό λΉ„μŠ·ν•˜κ²Œ, ~의 λΆ€λΆ„ μ§‘ν•©μ΄λΌλŠ” 의미둜 받아듀일 수 μžˆμŠ΅λ‹ˆλ‹€.

interface Vector1D { x: number; }
interface Vector2D extends Vector1D { y: number; }
interface Vector3D extends Vector2D { z: number; }

Vector3DλŠ” Vector2D의 μ„œλΈŒνƒ€μž…μ΄κ³  Vector2DλŠ” Vector1D의 μ„œλΈŒνƒ€μž…μž…λ‹ˆλ‹€.
extends ν‚€μ›Œλ“œλŠ” μ œλ„ˆλ¦­ νƒ€μž…μ—μ„œ ν•œμ •μžλ‘œλ„ 쓰이며, 이 λ¬Έλ§₯μ—μ„œλŠ” "~의 λΆ€λΆ„ 집합"을 μ˜λ―Έν•˜κΈ°λ„ ν•©λ‹ˆλ‹€.

function getKey<K extends string>(val: any, key: K) {
// ...
}

string의 λΆ€λΆ„ 집합 λ²•μœ„λ₯Ό κ°€μ§€λŠ” μ–΄λ– ν•œ νƒ€μž…μ΄ λ©λ‹ˆλ‹€. 이 νƒ€μž…μ€ string λ¦¬ν„°λŸ΄ νƒ€μž…, string λ¦¬ν„°λŸ΄ νƒ€μž…μ˜ μœ λ‹ˆμ˜¨, string μžμ‹ μ„ ν¬ν•¨ν•©λ‹ˆλ‹€.

getKey({}, 'x'); // 정상, string을 상속
getKey({}, Math.random() < 0.5 ? 'a' : 'b'); // 정상, string을 상속
getKey({}, document.title); // 정상
getKey({}, 12); // ~~~ '12' ν˜•μ‹μ˜ μΈμˆ˜λŠ” 'string' ν˜•μ‹μ˜ λ§€κ°œλ³€μˆ˜μ— 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

νƒ€μž…μ΄ κ°’μ˜ μ§‘ν•©μ΄λΌλŠ” 건, λ™μΌν•œ κ°’μ˜ 집합을 κ°€μ§€λŠ” 두 νƒ€μž…μ€ κ°™λ‹€λŠ” μ˜λ―Έκ°€ λ©λ‹ˆλ‹€. 두 νƒ€μž…μ΄ 의미적으둜 λ‹€λ₯΄κ³  μš°μ—°νžˆ 같은 λ²”μœ„λ₯Ό 가진닀고 ν•˜λ”λΌλ„, 같은 νƒ€μž…μ„ 두 번 μ •μ˜ν•  μ΄μœ λŠ” μ—†μŠ΅λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 8. νƒ€μž… 곡간과 κ°’ κ³΅κ°„μ˜ μ‹¬λ²Œ κ΅¬λΆ„ν•˜κΈ°β€‹

νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ‹¬λ²Œμ€ νƒ€μž… κ³΅κ°„μ΄λ‚˜ κ°’ 곡간 μ€‘μ˜ ν•œ 곳에 μ‘΄μž¬ν•©λ‹ˆλ‹€. μ‹¬λ²Œμ€ 이름이 같더라도 μ†ν•˜λŠ” 곡간에 따라 λ‹€λ₯Έ 것을 λ‚˜νƒ€λ‚Ό 수 있기 λ•Œλ¬Έμ— ν˜Όλž€μŠ€λŸ¬μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€.

interface Cylinder {
radius: number;
height: number;
}

const Cylinder = (radius: number, height: number) => ({ radius, height });

interface Cylinderμ—μ„œ CylinderλŠ” νƒ€μž…μœΌλ‘œ μ“°μž…λ‹ˆλ‹€. const Cylinderμ—μ„œ Cylinder와 이름은 κ°™μ§€λ§Œ κ°’μœΌλ‘œ 쓰이며, μ„œλ‘œ μ•„λ¬΄λŸ° 관련도 μ—†μŠ΅λ‹ˆλ‹€. 상황에 λ”°λΌμ„œ CylinderλŠ” νƒ€μž…μœΌλ‘œ 쓰일 μˆ˜λ„ 있고, κ°’μœΌλ‘œ 쓰일 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 이런 점은 가끔 였λ₯˜λ₯Ό μ•ΌκΈ°ν•©λ‹ˆλ‹€.

ν•œ μ‹¬λ²Œμ΄ νƒ€μž…μΈμ§€ κ°’μΈμ§€λŠ” μ–Έλœ» λ΄μ„œ μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. μ–΄λ–€ ν˜•νƒœλ‘œ μ“°μ΄λŠ”μ§€ λ¬Έλ§₯을 μ‚΄νŽ΄ μ•Œμ•„λ‚΄μ•Ό ν•©λ‹ˆλ‹€. λ§Žμ€ νƒ€μž… μ½”λ“œκ°€ κ°’ μ½”λ“œμ™€ λΉ„μŠ·ν•΄ 보이기 λ•Œλ¬Έμ— λ”λ”μš± ν˜Όλž€μŠ€λŸ½μŠ΅λ‹ˆλ‹€.

type T1 = typeof p; // νƒ€μž…μ€ Person
type T2 = typeof email; // νƒ€μž…μ€ (p: Person, subject: string, body: string) => Response

const v1 = typeof p; // 값은 "object"
const v2 = typeof email; // 값은 "function"

νƒ€μž…μ˜ κ΄€μ μ—μ„œ, typeofλŠ” 값을 μ½μ–΄μ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž…μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€. νƒ€μž… κ³΅κ°„μ˜ typeofλŠ” 보닀 큰 νƒ€μž…μ˜ μΌλΆ€λΆ„μœΌλ‘œ μ‚¬μš©ν•  수 있고, typeꡬ문으둜 이름을 λΆ™μ΄λŠ” μš©λ„λ‘œλ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ°’μ˜ κ΄€μ μ—μ„œ typeofλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ λŸ°νƒ€μž„μ˜ typeof μ—°μ‚°μžκ°€ λ©λ‹ˆλ‹€. κ°’ κ³΅κ°„μ˜ typeofλŠ” λŒ€μƒ μ‹¬λ²Œμ˜ λŸ°νƒ€μž„ νƒ€μž…μ„ κ°€λ¦¬ν‚€λŠ” λ¬Έμžμ—΄μ„ λ°˜ν™˜ν•˜λ©°, νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž…κ³ΌλŠ” λ‹€λ¦…λ‹ˆλ‹€. μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λŸ°νƒ€μž„ νƒ€μž… μ‹œμŠ€ν…œμ€ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ 정적 νƒ€μž… μ‹œμŠ€ν…œλ³΄λ‹€ 훨씬 κ°„λ‹¨ν•©λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž…μ˜ μ’…λ₯˜κ°€ 무수히 λ§Žμ€ 반면, μžλ°”μŠ€ν¬λ¦½νŠΈμ—λŠ” κ³Όκ±°λΆ€ν„° μ§€κΈˆκΉŒμ§€ 단 6개(string, number, boolean, undefined, object, function)의 λŸ°νƒ€μž„ νƒ€μž…λ§Œμ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€.

두 곡간 μ‚¬μ΄μ—μ„œ λ‹€λ₯Έ 의미λ₯Ό κ°€μ§€λŠ” μ½”λ“œ νŒ¨ν„΄λ“€μ΄ μžˆμŠ΅λ‹ˆλ‹€.

  • κ°’μœΌλ‘œ μ“°μ΄λŠ” thisλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ this ν‚€μ›Œλ“œμž…λ‹ˆλ‹€. νƒ€μž…μœΌλ‘œ μ“°μ΄λŠ” thisλŠ”, 일λͺ… "λ‹€ν˜•μ„± this"라고 λΆˆλ¦¬λŠ” this의 νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž…μž…λ‹ˆλ‹€.
  • κ°’μ—μ„œ &와 |λŠ” AND와 OR λΉ„νŠΈμ—°μ‚°μž…λ‹ˆλ‹€. νƒ€μž…μ—μ„œλŠ” μΈν„°λ ‰μ…˜κ³Ό μœ λ‹ˆμ˜¨μž…λ‹ˆλ‹€.
  • constλŠ” μƒˆ λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜μ§€λ§Œ, as constλŠ” λ¦¬ν„°λŸ΄ λ˜λŠ” λ¦¬ν„°λŸ΄ ν‘œν˜„μ‹μ˜ μΆ”λ‘œλœ νƒ€μž…μ„ λ°”κΏ‰λ‹ˆλ‹€.
  • extendsλŠ” μ„œλΈŒν΄λž˜μŠ€(class A extends B) λ˜λŠ” μ„œλΈŒνƒ€μž…(interface A extends B) λ˜λŠ” μ œλ„ˆλ¦­ νƒ€μž… ν•œμ •μž(Generic<T extends number>)λ₯Ό μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • in은 루프 λ˜λŠ” λ§€ν•‘λœ νƒ€μž…μ— λ“±μž₯ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 9. νƒ€μž… λ‹¨μ–Έλ³΄λ‹€λŠ” νƒ€μž… 선언을 μ‚¬μš©ν•˜κΈ°β€‹

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ λ³€μˆ˜μ— 값을 ν• λ‹Ήν•˜κ³  νƒ€μž…μ„ λΆ€μ—¬ν•˜λŠ” 방법은 두 κ°€μ§€μž…λ‹ˆλ‹€.

interface Person { name: string };

const alice: Person = { name: 'Alice' };
const bob = { name: 'Bob' } as Person;

첫 번째 alice: Person은 λ³€μˆ˜μ— νƒ€μž… 선언을 λΆ™μ—¬μ„œ κ·Έ 값이 μ„ μ–Έλœ νƒ€μž…μž„μ„ λͺ…μ‹œν•©λ‹ˆλ‹€. 두 번째 as Person은 νƒ€μž… 단언을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. 그러면 νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μΆ”λ‘ ν•œ νƒ€μž…μ΄ μžˆλ”λΌλ„ Person νƒ€μž…μœΌλ‘œ κ°„μ£Όν•©λ‹ˆλ‹€.
νƒ€μž… 단언보닀 νƒ€μž… 선언을 μ‚¬μš©ν•˜λŠ” 게 λ‚«μŠ΅λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” λ‹€μŒ μ½”λ“œμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

const alice: Person = {}; // ~~~ 'Person' μœ ν˜•μ— ν•„μš”ν•œ 'name' 속성이 '{}' μœ ν˜•μ— μ—†μŠ΅λ‹ˆλ‹€.
const bob = {} as Person; // 였λ₯˜ μ—†μŒ

νƒ€μž… 선언은 ν• λ‹Ήλ˜λŠ” 값이 ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ§Œμ‘±ν•˜λŠ”μ§€ κ²€μ‚¬ν•©λ‹ˆλ‹€. μ•žμ˜ μ˜ˆμ œμ—μ„œλŠ” κ·ΈλŸ¬μ§€ λͺ»ν–ˆκΈ° λ•Œλ¬Έμ— νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 였λ₯˜λ₯Ό ν‘œμ‹œν–ˆμŠ΅λ‹ˆλ‹€. νƒ€μž… 단언은 κ°•μ œλ‘œ νƒ€μž…μ„ μ§€μ •ν–ˆμœΌλ‹ˆ νƒ€μž… μ²΄μ»€μ—κ²Œ 였λ₯˜λ₯Ό λ¬΄μ‹œν•˜λΌκ³  ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

νƒ€μž… 단언이 κΌ­ ν•„μš”ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄, μ•ˆμ „μ„± 체크도 λ˜λŠ” νƒ€μž… 선언을 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

ν™”μ‚΄ν‘œ ν•¨μˆ˜μ˜ νƒ€μž… 선언은 μΆ”λ‘ λœ νƒ€μž…μ΄ λͺ¨ν˜Έν•  λ•Œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μŒ μ½”λ“œμ—μ„œ Person μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜κ³  μ‹Άλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

const people = ['alice', 'bob', 'jan'].map(name => ({ name }));
// Person[]을 μ›ν–ˆμ§€λ§Œ κ²°κ³ΌλŠ” { name: string; }[]

λ‹€μŒκ³Ό 같이 ν™”μ‚΄ν‘œ ν•¨μˆ˜ μ•ˆμ—μ„œ νƒ€μž…κ³Ό ν•¨κ»˜ λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λŠ” 것이 κ°€μž₯ μ§κ΄€μ μž…λ‹ˆλ‹€.

const people = ['alice', 'bob', 'jan'].map(name => {
const person: Person = { name };
return person;
}); // νƒ€μž…μ€ Person[]

μ½”λ“œλ₯Ό μ’€ 더 κ°„κ²°ν•˜κ²Œ 보이기 μœ„ν•΄ λ³€μˆ˜ λŒ€μ‹  ν™”μ‚΄ν‘œ ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…μ„ μ„ μ–Έν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

const people = ['alice', 'bob', 'jan'].map((name): Person => ({ name })); // νƒ€μž…μ€ Person[]

이 μ½”λ“œλŠ” λ°”λ‘œ μ•žμ˜ λ²ˆμž‘ν•œ 버전과 λ™μΌν•œ 체크λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ μ†Œκ΄„ν˜ΈλŠ” 맀우 μ€‘μš”ν•œ 의미λ₯Ό μ§€λ‹™λ‹ˆλ‹€. name의 νƒ€μž…μ΄ μ—†κ³ , λ°˜ν™˜ νƒ€μž…μ΄ Person이라고 λͺ…μ‹œν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ (name: Person)은 name의 νƒ€μž…μ΄ Personμž„μ„ λͺ…μ‹œν•˜κ³  λ°˜ν™˜ νƒ€μž…μ€ μ—†κΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.
λ‹€μŒ μ½”λ“œλŠ” μ΅œμ’…μ μœΌλ‘œ μ›ν•˜λŠ” νƒ€μž…μ„ 직접 λͺ…μ‹œν•˜κ³ , νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ ν• λ‹Ήλ¬Έμ˜ μœ νš¨μ„±μ„ κ²€μ‚¬ν•˜κ²Œ ν•©λ‹ˆλ‹€.

const people: Person[] = ['alice', 'bob', 'jan'].map((name): Person => ({ name })); // νƒ€μž…μ€ Person[]

κ·ΈλŸ¬λ‚˜ ν•¨μˆ˜ 호좜 체이닝이 μ—°μ†λœ κ³³μ—λŠ” 체이닝 μ‹œμž‘μ—μ„œλΆ€ν„° λͺ…λͺ…λœ νƒ€μž…μ„ κ°€μ Έμ•Ό ν•©λ‹ˆλ‹€. κ·Έλž˜μ•Ό μ •ν™•ν•œ 곳에 였λ₯˜κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.

λ‹€μŒμœΌλ‘œ νƒ€μž… 단언이 κΌ­ ν•„μš”ν•œ 경우λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. νƒ€μž… 단언은 νƒ€μž… 체컀가 μΆ”λ‘ ν•œ νƒ€μž…λ³΄λ‹€ μ—¬λŸ¬λΆ„μ΄ νŒλ‹¨ν•˜λŠ” νƒ€μž…μ΄ 더 μ •ν™•ν•  λ•Œ μ˜λ―Έκ°€ μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, DOM μ—˜λ¦¬λ¨ΌνŠΈμ— λŒ€ν•΄μ„œλŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈλ³΄λ‹€ μ—¬λŸ¬λΆ„μ΄ 더 μ •ν™•νžˆ μ•Œκ³  μžˆμ„ κ²λ‹ˆλ‹€.

document.querySelector('#myButton').addEventListener('click', (e) => {
e.currentTarget // νƒ€μž…μ€ EventTarget
const button = e.currentTarget as HTMLButtonElement;
button // νƒ€μž…μ€ HTMLButtonElement
});

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” DOM에 μ ‘κ·Όν•  수 μ—†κΈ° λ•Œλ¬Έμ— #myButton이 λ²„νŠΌ μ—˜λ¦¬λ¨ΌνŠΈμΈμ§€ μ•Œμ§€ λͺ»ν•©λ‹ˆλ‹€. 그리고 이벀트의 currentTarget이 같은 λ²„νŠΌμ΄μ–΄μ•Ό ν•˜λŠ” 것도 μ•Œμ§€ λͺ»ν•©λ‹ˆλ‹€. μš°λ¦¬λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ•Œμ§€ λͺ»ν•˜λŠ” 정보λ₯Ό 가지고 있기 λ•Œλ¬Έμ— μ—¬κΈ°μ„œλŠ” νƒ€μž… 단언문을 μ“°λŠ” 것이 νƒ€λ‹Ήν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 10. 객체 래퍼 νƒ€μž… ν”Όν•˜κΈ°β€‹

κΈ°λ³Έν˜•λ“€μ€ λΆˆλ³€μ΄λ©° λ©”μ„œλ“œλ₯Ό 가지지 μ•ŠλŠ”λ‹€λŠ” μ μ—μ„œ 객체와 κ΅¬λΆ„λ©λ‹ˆλ‹€. 그런데 κΈ°λ³Έν˜•μΈ string의 경우 λ©”μ„œλ“œλ₯Ό 가지고 μžˆλŠ” κ²ƒμ²˜λŸΌ λ³΄μž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ 사싀 string의 λ©”μ„œλ“œκ°€ μ•„λ‹ˆλ©°, string을 μ‚¬μš©ν•  λ•Œ μžλ°”μŠ€ν¬λ¦½νŠΈ λ‚΄λΆ€μ μœΌλ‘œ λ§Žμ€ λ™μž‘μ΄ μΌμ–΄λ‚©λ‹ˆλ‹€. string κΈ°λ³Έν˜•μ—λŠ” λ©”μ„œλ“œκ°€ μ—†μ§€λ§Œ, μžλ°”μŠ€ν¬λ¦½νŠΈμ—λŠ” λ©”μ„œλ“œλ₯Ό κ°€μ§€λŠ” String 객체 νƒ€μž…μ΄ μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” κΈ°λ³Έν˜•κ³Ό 객체 νƒ€μž…μ„ μ„œλ‘œ 자유둭게 λ³€ν™˜ν•©λ‹ˆλ‹€. string κΈ°λ³Έν˜•μ— charAt 같은 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  λ•Œ, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” κΈ°λ³Έν˜•μ„ String 객체둜 λž˜ν•‘ν•˜κ³ , λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κ³ , λ§ˆμ§€λ§‰μ— λž˜ν•‘ν•œ 객체λ₯Ό λ²„λ¦½λ‹ˆλ‹€.

λ§Œμ•½ String.prototype을 λͺ½ν‚€-νŒ¨μΉ˜ν•œλ‹€λ©΄ μ•žμ„œ μ„€λͺ…ν•œ 내뢀적인 λ™μž‘λ“€μ„ κ΄€μ°°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ½ν‚€-νŒ¨μΉ˜λž€ λŸ°νƒ€μž„μ— ν”„λ‘œκ·Έλž¨μ˜ μ–΄λ–€ κΈ°λŠ₯을 μˆ˜μ •ν•΄μ„œ μ‚¬μš©ν•˜λŠ” 기법을 μ˜λ―Έν•©λ‹ˆλ‹€. μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” 주둜 ν”„λ‘œν† νƒ€μž…μ„ λ³€κ²½ν•˜λŠ” 것이 ν•΄λ‹Ήν•©λ‹ˆλ‹€.

λ©”μ„œλ“œ λ‚΄μ˜ thisλŠ” string κΈ°λ³Έν˜•μ΄ μ•„λ‹Œ String 객체 λž˜νΌμž…λ‹ˆλ‹€. String 객체λ₯Ό 직접 생성할 μˆ˜λ„ 있으며, string κΈ°λ³Έν˜•μ²˜λŸΌ λ™μž‘ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ string κΈ°λ³Έν˜•κ³Ό String 객체 λž˜νΌκ°€ 항상 λ™μΌν•˜κ²Œ λ™μž‘ν•˜λŠ” 것은 μ•„λ‹™λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, String κ°μ²΄λŠ” 였직 자기 μžμ‹ ν•˜κ³ λ§Œ λ™μΌν•©λ‹ˆλ‹€.

"hello" === new String("hello"); // false
new String("hello") === new String("hello"); // false

πŸ₯• μ•„μ΄ν…œ 11. μž‰μ—¬ 속성 체크의 ν•œκ³„ μΈμ§€ν•˜κΈ°β€‹

νƒ€μž…μ΄ λͺ…μ‹œλœ λ³€μˆ˜μ— 객체 λ¦¬ν„°λŸ΄μ„ ν• λ‹Ήν•  λ•Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•΄λ‹Ή νƒ€μž…μ˜ 속성이 μžˆλŠ”μ§€, 그리고 'κ·Έ μ™Έμ˜ 속성은 μ—†λŠ”μ§€' ν™•μΈν•©λ‹ˆλ‹€.

interface Room {
numDoors: number;
ceilingHeightFt: number;
}

const r: Room = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present', // 개체 λ¦¬ν„°λŸ΄μ€ μ•Œλ €μ§„ μ†μ„±λ§Œ 지정할 수 있으며 'Room' ν˜•μ‹μ— 'elephant'κ°€ μ—†μŠ΅λ‹ˆλ‹€.
}

Room νƒ€μž…μ— μƒλš±λ§žκ²Œ elephant 속성이 μžˆλŠ” 것이 μ–΄μƒ‰ν•˜κΈ΄ ν•˜μ§€λ§Œ, ꡬ쑰적 타이핑 κ΄€μ μœΌλ‘œ 생각해 보면 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•Šμ•„μ•Ό ν•©λ‹ˆλ‹€. μž„μ‹œ λ³€μˆ˜λ₯Ό λ„μž…ν•΄ 보면 μ•Œ 수 μžˆλŠ”λ°, obj κ°μ²΄λŠ” Room νƒ€μž…μ— 할당이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

const obj = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
};

const r: Room = obj; // 정상

obj νƒ€μž…μ€ Room νƒ€μž…μ˜ λΆ€λΆ„ 집합을 ν¬ν•¨ν•˜λ―€λ‘œ, Room에 ν• λ‹Ή κ°€λŠ₯ν•˜λ©΄ νƒ€μž… 체컀도 ν†΅κ³Όν•©λ‹ˆλ‹€.

μ•ž 두 예제의 차이점을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. 첫 번째 μ˜ˆμ œμ—μ„œλŠ”, ꡬ쑰적 νƒ€μž… μ‹œμŠ€ν…œμ—μ„œ λ°œμƒν•  수 μžˆλŠ” μ€‘μš”ν•œ μ’…λ₯˜μ˜ 였λ₯˜λ₯Ό μž‘μ„ 수 μžˆλ„λ‘ μž‰μ—¬ 속성 μ²΄ν¬λΌλŠ” 과정이 μˆ˜ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μž‰μ—¬ 속성 체크 μ—­μ‹œ 쑰건에 따라 λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” ν•œκ³„κ°€ 있고, 톡상적인 ν• λ‹Ή κ°€λŠ₯ 검사와 ν•¨κ»˜ 쓰이면 ꡬ쑰적 타이핑이 무엇인지 ν˜ΌλΌμŠ€λŸ¬μ›Œμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€. μž‰μ—¬ 속성 체크가 ν• λ‹Ή κ°€λŠ₯ κ²€μ‚¬μ™€λŠ” λ³„λ„μ˜ κ³Όμ •μ΄λΌλŠ” 것을 μ•Œμ•„μ•Ό νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž… μ‹œμŠ€ν…œμ— λŒ€ν•œ κ°œλ…μ„ μ •ν™•νžˆ μž‘μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

μž‰μ—¬ 속성 μ²΄ν¬λŠ” ꡬ쑰적 타이핑 μ‹œμŠ€ν…œμ—μ„œ ν—ˆμš©λ˜λŠ” 속성 μ΄λ¦„μ˜ μ˜€νƒ€ 같은 μ‹€μˆ˜λ₯Ό μž‘λŠ” 데 효과적인 λ°©λ²•μž…λ‹ˆλ‹€. μž‰μ—¬ 속성 μ²΄ν¬λŠ” 였λ₯˜λ₯Ό μ°ΎλŠ” 효과적인 λ°©λ²•μ΄μ§€λ§Œ, νƒ€μž…μŠ€ν¬λ¦½νŠΈ νƒ€μž… 체컀가 μˆ˜ν–‰ν•˜λŠ” 일반적인 ꡬ쑰적 ν• λ‹Ή κ°€λŠ₯μ„± 체크와 역할이 λ‹€λ¦…λ‹ˆλ‹€. ν• λ‹Ήμ˜ κ°œλ…μ„ μ •ν™•νžˆ μ•Œμ•„μ•Ό μž‰μ—¬ 속성 체크와 일반적인 ꡬ쑰적 ν• λ‹Ή κ°€λŠ₯μ„± 체크λ₯Ό ꡬ뢄할 수 μžˆμŠ΅λ‹ˆλ‹€.

μž‰μ—¬ 속성 μ²΄ν¬μ—λŠ” ν•œκ³„κ°€ μžˆμŠ΅λ‹ˆλ‹€. μž„μ‹œ λ³€μˆ˜λ₯Ό λ„μž…ν•˜λ©΄ μž‰μ—¬ 속성 체크λ₯Ό κ±΄λ„ˆλ›Έ 수 μžˆλ‹€λŠ” 점을 κΈ°μ–΅ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 12. ν•¨μˆ˜ ν‘œν˜„μ‹μ— νƒ€μž… μ μš©ν•˜κΈ°β€‹

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” ν•¨μˆ˜ λ¬Έμž₯κ³Ό ν•¨μˆ˜ ν‘œν˜„μ‹μ„ λ‹€λ₯΄κ²Œ μΈμ‹ν•©λ‹ˆλ‹€.

function rollDice1(sides: number): number { /* ... */ } // λ¬Έμž₯
const rollDice2 = function(sides: number): number { /* ... */ }; // ν‘œν˜„μ‹
const rollDice3 = (sides: number): number => { /* ... */ }; // ν‘œν˜„μ‹

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” ν•¨μˆ˜ ν‘œν˜„μ‹μ„ μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜λΆ€ν„° λ°˜ν™˜κ°’κΉŒμ§€ 전체λ₯Ό ν•¨μˆ˜ νƒ€μž…μœΌλ‘œ μ„ μ–Έν•˜μ—¬ ν•¨μˆ˜ ν‘œν˜„μ‹μ— μž¬μ‚¬μš©ν•  수 μžˆλ‹€λŠ” μž₯점이 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

type DiceRollFn = (sides: number) => number;
const rollDice: DiceRollFn = sides => { /* ... */ };

νŽΈμ§‘κΈ°μ—μ„œ sides에 마우슀λ₯Ό 올렀 보면, νƒ€μž…μŠ€ν¬λ¦·νŠΈμ—μ„œλŠ” 이미 sides의 νƒ€μž…μ„ number둜 μΈμ‹ν•˜κ³  μžˆλ‹€λŠ” κ±Έ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. ν•¨μˆ˜ νƒ€μž…μ˜ 선언은 λΆˆν•„μš”ν•œ μ½”λ“œμ˜ λ°˜λ³΅μ„ μ€„μž…λ‹ˆλ‹€.

function add(a: number, b: number) { return a + b; }

type BinaryFn = (a: number, b: number) => number;
const add: BinaryFn = (a, b) => a + b;

이 μ˜ˆμ œλŠ” ν•¨μˆ˜ νƒ€μž… 선언을 μ΄μš©ν–ˆλ˜ μ˜ˆμ œλ³΄λ‹€ νƒ€μž… ꡬ문이 μ μŠ΅λ‹ˆλ‹€. ν•¨μˆ˜ κ΅¬ν˜„λΆ€λ„ λΆ„λ¦¬λ˜μ–΄ μžˆμ–΄ 둜직이 보닀 λΆ„λͺ…ν•΄μ§‘λ‹ˆλ‹€. λ§Œμ•½ μ—¬λŸ¬λΆ„μ΄ 라이브러리λ₯Ό 직접 λ§Œλ“€κ³  μžˆλ‹€λ©΄, 곡톡 콜백 ν•¨μˆ˜λ₯Ό μœ„ν•œ νƒ€μž… 선언을 μ œκ³΅ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜μ— νƒ€μž… 선언을 ν•˜λŠ” 것보닀 ν•¨μˆ˜ ν‘œν˜„μ‹ 전체 νƒ€μž…μ„ μ •μ˜ν•˜λŠ” 것이 μ½”λ“œλ„ κ°„κ²°ν•˜κ³  μ•ˆμ „ν•©λ‹ˆλ‹€. λ‹€λ₯Έ ν•¨μˆ˜μ˜ μ‹œκ·Έλ‹ˆμ²˜μ™€ λ™μΌν•œ νƒ€μž…μ„ κ°€μ§€λŠ” μƒˆ ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜κ±°λ‚˜, λ™μΌν•œ νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λ₯Ό κ°€μ§€λŠ” μ—¬λŸ¬ 개의 ν•¨μˆ˜λ₯Ό μž‘μ„±ν•  λ•ŒλŠ” λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…κ³Ό λ°˜ν™˜ νƒ€μž…μ„ λ°˜λ³΅ν•΄μ„œ μž‘μ„±ν•˜μ§€ 말고 ν•¨μˆ˜ μ „μ²΄μ˜ νƒ€μž… 선언을 μ μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 13. νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€μ˜ 차이점 μ•ŒκΈ°β€‹

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ λͺ…λͺ…λœ νƒ€μž…μ„ μ •μ˜ν•˜λŠ” 방법은 두 가지가 μžˆμŠ΅λ‹ˆλ‹€.

type TState = {
name: string;
capital: string;
}

interface IState {
name: string;
capital: string;
}

νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€ 사이에 μ‘΄μž¬ν•˜λŠ” 차이λ₯Ό λΆ„λͺ…ν•˜κ²Œ μ•Œκ³ , 같은 μƒν™©μ—μ„œλŠ” λ™μΌν•œ λ°©λ²•μœΌλ‘œ λͺ…λͺ…λœ νƒ€μž…μ„ μ •μ˜ν•΄ 일관성을 μœ μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

λ¨Όμ €, μΈν„°νŽ˜μ΄μŠ€ μ„ μ–Έκ³Ό νƒ€μž… μ„ μ–Έμ˜ λΉ„μŠ·ν•œ 점에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. λͺ…λͺ…λœ νƒ€μž…μ€ μΈν„°νŽ˜μ΄μŠ€λ‘œ μ •μ˜ν•˜λ“  νƒ€μž…μœΌλ‘œ μ •μ˜ν•˜λ“  μƒνƒœμ—λŠ” 차이가 μ—†μŠ΅λ‹ˆλ‹€. λ§Œμ•½ IState와 TStateλ₯Ό μΆ”κ°€ 속성과 ν•¨κ»˜ ν• λ‹Ήν•œλ‹€λ©΄ λ™μΌν•œ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

인덱슀 μ‹œκ·Έλ‹ˆμ²˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ™€ νƒ€μž…μ—μ„œ λͺ¨λ‘ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type TDict = {
[key: string]: string
};

interface IDict {
[key: string]: string;
}

λ˜ν•œ ν•¨μˆ˜ νƒ€μž…λ„ μΈν„°νŽ˜μ΄μŠ€λ‚˜ νƒ€μž…μœΌλ‘œ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type TFn = (x: number) => string;
interface IFn {
(x: number): string;
}

const toStrT: TFn = (x) => '' + x;
const toStrT: IFn = (x) => '' + x;

νƒ€μž… 별칭과 μΈν„°νŽ˜μ΄μŠ€λŠ” λͺ¨λ‘ μ œλ„ˆλ¦­μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

type TPair<T> = {
first: T;
second: T;
}

interface IPair<T> {
first: T;
second: T;
}

μΈν„°νŽ˜μ΄μŠ€λŠ” νƒ€μž…μ„ ν™•μž₯ν•  수 있으며, νƒ€μž…μ€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™•μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

interface IStateWithPop extends TState {
population: number;
}

type TStateWithPop = IState & { population: number; };

μ—¬κΈ°μ„œ μ£Όμ˜ν•  점은 μΈν„°νŽ˜μ΄μŠ€λŠ” μœ λ‹ˆμ˜¨ νƒ€μž… 같은 λ³΅μž‘ν•œ νƒ€μž…μ„ ν™•μž₯ν•˜μ§€ λͺ»ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. λ³΅μž‘ν•œ νƒ€μž…μ„ ν™•μž₯ν•˜κ³  μ‹Άλ‹€λ©΄ νƒ€μž…κ³Ό &λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.
ν•œνŽΈ 클래슀λ₯Ό κ΅¬ν˜„ν•  λ•ŒλŠ”, νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€ λ‘˜ λ‹€ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class StateT implements TState {
name: string = '';
capital: string = '';
}

class StateI implements IState {
name: string = '';
capital: string = '';
}

μ΄μ œλΆ€ν„°λŠ” νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€μ˜ λ‹€λ₯Έ 점듀을 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

μœ λ‹ˆμ˜¨ νƒ€μž…μ€ μžˆμ§€λ§Œ μœ λ‹ˆμ˜¨ μΈν„°νŽ˜μ΄μŠ€λΌλŠ” κ°œλ…μ€ μ—†μŠ΅λ‹ˆλ‹€.

type AorB = 'a' | 'b';

μΈν„°νŽ˜μ΄μŠ€λŠ” νƒ€μž…μ„ ν™•μž₯ν•  수 μžˆμ§€λ§Œ, μœ λ‹ˆμ˜¨μ€ ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 그런데 μœ λ‹ˆμ˜¨ νƒ€μž…μ„ ν™•μž₯ν•˜λŠ” 게 ν•„μš”ν•  λ•Œκ°€ μžˆμŠ΅λ‹ˆλ‹€.

type Input = { /* ... */};
type Output = { /* ... */};
interface VariableMap {
[name: string]: Input | Output;
}

λ˜λŠ” μœ λ‹ˆμ˜¨ νƒ€μž…μ— name 속성을 뢙인 νƒ€μž…μ„ λ§Œλ“€ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

type NamedVariable = (Input | Output) & { name: string };

이 νƒ€μž…μ€ μΈν„°νŽ˜μ΄μŠ€λ‘œ ν‘œν˜„ν•  수 μ—†μŠ΅λ‹ˆλ‹€. type ν‚€μ›Œλ“œλŠ” 일반적으둜 interface보닀 μ“°μž„μƒˆκ°€ λ§ŽμŠ΅λ‹ˆλ‹€. type ν‚€μ›Œλ“œλŠ” μœ λ‹ˆμ˜¨μ΄ 될 μˆ˜λ„ 있고, λ§€ν•‘λœ νƒ€μž… λ˜λŠ” 쑰건뢀 νƒ€μž… 같은 κ³ κΈ‰ κΈ°λŠ₯에 ν™œμš©λ˜κΈ°λ„ ν•©λ‹ˆλ‹€.
νŠœν”Œκ³Ό λ°°μ—΄ νƒ€μž…λ„ type ν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•΄ 더 κ°„κ²°ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type Pair = [number, number];
type StringList = string[];
type NamedNums = [string, ...number[]];

μΈν„°νŽ˜μ΄μŠ€λ‘œλ„ νŠœν”Œκ³Ό λΉ„μŠ·ν•˜κ²Œ κ΅¬ν˜„ν•  수 μžˆκΈ°λŠ” ν•©λ‹ˆλ‹€.

interface Tuple {
0: number;
1: number;
length: 2;
}
const t: Tuple = [10, 20]; // 정상

κ·ΈλŸ¬λ‚˜ μΈν„°νŽ˜μ΄μŠ€λ‘œ νŠœν”Œκ³Ό λΉ„μŠ·ν•˜κ²Œ κ΅¬ν˜„ν•˜λ©΄ νŠœν”Œμ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” concat 같은 λ©”μ„œλ“œλ“€μ„ μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ νŠœν”Œμ€ type ν‚€μ›Œλ“œλ‘œ κ΅¬ν˜„ν•˜λŠ” 것이 λ‚«μŠ΅λ‹ˆλ‹€.

반면 μΈν„°νŽ˜μ΄μŠ€λŠ” νƒ€μž…μ— μ—†λŠ” λͺ‡ 가지 κΈ°λŠ₯이 μžˆμŠ΅λ‹ˆλ‹€. 그쀑 ν•˜λ‚˜λ‘œ λ°”λ‘œ 보강(augment)이 κ°€λŠ₯ν•˜λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 이번 μ•„μ΄ν…œ μ²˜μŒμ— λ“±μž₯ν–ˆλ˜ State μ˜ˆμ œμ— population ν•„λ“œλ₯Ό μΆ”κ°€ν•  λ•Œ 보강 기법을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

interface IState {
name: string;
capital: string;
}

interface IState {
population: number;
}

const wyoming: IState = {
name: 'Wyoming',
capital: 'Cheyenne',
population: 500_000
}; // 정상

이 예제처럼 속성을 ν™•μž₯ν•˜λŠ” 것을 μ„ μ–Έ 병합(declaration merging)이라고 ν•©λ‹ˆλ‹€. μ„ μ–Έ 병합은 주둜 νƒ€μž… μ„ μ–Έ νŒŒμΌμ—μ„œ μ‚¬μš©λ©λ‹ˆλ‹€. λ”°λΌμ„œ νƒ€μž… μ„ μ–Έ νŒŒμΌμ„ μž‘μ„±ν•  λ•ŒλŠ” μ„ μ–Έ 병합을 μ§€μ›ν•˜κΈ° μœ„ν•΄ λ°˜λ“œμ‹œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λ©° ν‘œμ€€μ„ 따라야 ν•©λ‹ˆλ‹€. νƒ€μž… μ„ μ–Έμ—λŠ” μ‚¬μš©μžκ°€ μ±„μ›Œμ•Ό ν•˜λŠ” λΉˆν‹ˆμ΄ μžˆμ„ 수 μžˆλŠ”λ°, λ°”λ‘œ 이 μ„ μ–Έ 병합이 κ·Έλ ‡μŠ΅λ‹ˆλ‹€.

병합은 μ„ μ–Έμ²˜λŸΌ 일반적인 μ½”λ“œλΌμ„œ μ–Έμ œλ“ μ§€ κ°€λŠ₯ν•˜λ‹€λŠ” 것을 μ•Œκ³  μžˆμ–΄μ•Όν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ ν”„λ‘œνΌν‹°κ°€ μΆ”κ°€λ˜λŠ” 것을 μ›ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ μΈν„°νŽ˜μ΄μŠ€ λŒ€μ‹  νƒ€μž…μ„ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

이번 μ•„μ΄ν…œμ˜ 처음 질문으둜 λŒμ•„κ°€ νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€ 쀑 μ–΄λŠ 것을 μ‚¬μš©ν•΄μ•Ό 할지 결둠을 λ‚΄λ € λ³΄κ² μŠ΅λ‹ˆλ‹€. λ³΅μž‘ν•œ νƒ€μž…μ΄λΌλ©΄ κ³ λ―Όν•  것도 없이 νƒ€μž… 별칭을 μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€, 두 가지 λ°©λ²•μœΌλ‘œ λͺ¨λ‘ ν‘œν˜„ν•  수 μžˆλŠ” κ°„λ‹¨ν•œ 객체 νƒ€μž…μ΄λΌλ©΄ 일관성과 λ³΄κ°•μ˜ κ΄€μ •μ—μ„œ κ³ λ €ν•΄ 보아야 ν•©λ‹ˆλ‹€. μ•Œκ΄€λ˜κ²Œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” μ½”λ“œλ² μ΄μŠ€μ—μ„œ μž‘μ—…ν•˜κ³  μžˆλ‹€λ©΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜κ³ , μΌκ΄€λ˜κ²Œ νƒ€μž…μ„ μ‚¬μš© 쀑이라면 νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.
아직 μŠ€νƒ€μΌμ΄ ν™•λ¦½λ˜μ§€ μ•Šμ€ ν”„λ‘œμ νŠΈλΌλ©΄, ν–₯μš°μ— λ³΄κ°•μ˜ κ°€λŠ₯성이 μžˆμ„μ§€ 생각해 봐야 ν•©λ‹ˆλ‹€. μ–΄λ–€ API에 λŒ€ν•œ νƒ€μž… 선언을 μž‘μ„±ν•΄μ•Ό ν•œλ‹€λ©΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 게 μ’‹μŠ΅λ‹ˆλ‹€. APIκ°€ 변경될 λ•Œ μ‚¬μš©μžκ°€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 μƒˆλ‘œμš΄ ν•„λ“œλ₯Ό 병합할 수 μžˆμ–΄ μœ μš©ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ ν”„λ‘œμ νŠΈ λ‚΄λΆ€μ μœΌλ‘œ μ‚¬μš©λ˜λŠ” νƒ€μž…μ— μ„ μ–Έ 병합이 λ°œμƒν•˜λŠ” 것은 잘λͺ»λœ μ„€κ³„μž…λ‹ˆλ‹€. λ”°λΌμ„œ 이럴 λ•ŒλŠ” νƒ€μž…μ„ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 14. νƒ€μž… μ—°μ‚°κ³Ό μ œλ„ˆλ¦­ μ‚¬μš©μœΌλ‘œ 반볡 쀄이기​

νƒ€μž… 쀑볡은 μ½”λ“œ μ€‘λ³΅λ§ŒνΌ λ§Žμ€ 문제λ₯Ό λ°œμƒμ‹œν‚΅λ‹ˆλ‹€. νƒ€μž…μ—μ„œ 쀑볡이 더 ν”ν•œ 이유 쀑 ν•˜λ‚˜λŠ” 곡유된 νŒ¨ν„΄μ„ μ œκ±°ν•˜λŠ” λ©”μ»€λ‹ˆμ¦˜μ΄ κΈ°μ‘΄ μ½”λ“œμ—μ„œ ν•˜λ˜ 것과 비ꡐ해 덜 μ΅μˆ™ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ νƒ€μž… 간에 λ§€ν•‘ν•˜λŠ” 방법을 읡히면, νƒ€μž… μ •μ˜μ—μ„œλ„ DRY의 μž₯점은 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
λ°˜λ³΅μ„ μ€„μ΄λŠ” κ°€μž₯ κ°„λ‹¨ν•œ 방법은 νƒ€μž…μ— 이름을 λΆ™μ΄λŠ” κ²ƒμž…λ‹ˆλ‹€. λ‹€μŒ 예제의 거리 계산 ν•¨μˆ˜μ—λŠ” νƒ€μž…μ΄ 반볡적으둜 λ“±μž₯ν•©λ‹ˆλ‹€.

function distance(a: { x: number. y: number }, b: { x: number, y: number }) {
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}

μ½”λ“œλ₯Ό μˆ˜μ •ν•΄ νƒ€μž…μ— 이름을 λΆ™μ—¬ λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface Point2D {
x: number;
y: number;
}

function distance(a: Point2D, b: Point2D) { /* ... */}

μ€‘λ³΅λœ νƒ€μž…μ€ μ’…μ’… 문법에 μ˜ν•΄μ„œ 가렀지기도 ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λͺ‡λͺ‡ ν•¨μˆ˜κ°€ 같은 νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λ₯Ό κ³΅μœ ν•˜κ³  μžˆλ‹€κ³  ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

function get(url: string, opts: Options): Promise<Response> { /* ... */ }
function post(url: string, opts: Options): Promise<Response> { /* ... */ }

그러면 ν•΄λ‹Ή μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λͺ…λͺ…λœ νƒ€μž…μœΌλ‘œ 뢄리해 λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

type HTTPFunction = (url: string, opts: Options) => Promise<Response>;
const get: HTTPFunction(url, opts) => { /* ... */ };
const post: HTTPFunction(url, opts) => { /* ... */ };

만걍 두 μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„λ“œμ˜ λΆ€λΆ„ 집합을 κ³΅μœ ν•œλ‹€λ©΄, 곡톡 ν•„λ“œλ§Œ κ³¨λΌμ„œ 기반 클래슀둜 뢄리해 λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
이미 μ‘΄μž¬ν•˜λŠ” νƒ€μž…μ„ ν™•μž₯ν•˜λŠ” κ²½μš°μ—, μΌλ°˜μ μ΄μ§€λŠ” μ•Šμ§€λ§Œ μ΄ν„°μ„Ήμ…˜ μ—°μ‚°μž(&)을 μ“Έ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

type PersonWithBirthDate = Person & { birth: Date };

이런 기법은 μœ λ‹ˆμ˜¨ νƒ€μž…(ν™•μž₯ν•  수 μ—†λŠ”)에 속성을 μΆ”κ°€ν•˜λ €κ³  ν•  λ•Œ 특히 μœ μš©ν•©λ‹ˆλ‹€.

전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μƒνƒœλ₯Ό ν‘œν˜„ν•˜λŠ” Stateνƒ€μž…κ³Ό 단지 λΆ€λΆ„λ§Œ ν‘œν˜„ν•˜λŠ” TopNavStateκ°€ μžˆλŠ” 경우λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface State {
userId: string;
pageTitle: string;
recentFiles: string[];
pageContents: string;
}

interface TopNavState {
userId: string;
pageTitle: string;
recentFiles: string[];
}

TopNavStateλ₯Ό ν™•μž₯ν•˜μ—¬ Stateλ₯Ό κ΅¬μ„±ν•˜κΈ°λ³΄λ‹€, State의 λΆ€λΆ„ μ§‘ν•©μœΌλ‘œ TopNavStateλ₯Ό μ •μ˜ν•˜λŠ” 것이 λ°”λžŒμ§ν•΄ λ³΄μž…λ‹ˆλ‹€. 이 방법이 전체 μ•±μ˜ μƒνƒœλ₯Ό ν•˜λ‚˜μ˜ μΈν„°νŽ˜μ΄μŠ€λ‘œ μœ μ§€ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. Stateλ₯Ό μΈλ±μ‹±ν•˜μ—¬ μ†μ„±μ˜ νƒ€μž…μ—μ„œ 쀑볡을 μ œκ±°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type TopNavState = {
userId: State['userId'];
pageTitle: State['pageTitle'];
recentFiles: State['recentFiles'];
}

κ·ΈλŸ¬λ‚˜ μ—¬μ „νžˆ λ°˜λ³΅λ˜λŠ” μ½”λ“œκ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€. μ΄λ•Œ λ§€ν•‘λœ νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μ’€ 더 λ‚˜μ•„μ§‘λ‹ˆλ‹€.

type TopNavState = {
[k in 'userId' | 'pageTitle' | 'recentFiles']: State[k]
};

λ§€ν•‘λœ νƒ€μž…μ€ λ°°μ—΄μ˜ ν•„λ“œλ₯Ό 루프 λ„λŠ” 것과 같은 λ°©μ‹μž…λ‹ˆλ‹€. 이 νŒ¨ν„΄μ€ ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλ„ 일반적으둜 찾을 수 있으며, Pick이라고 ν•©λ‹ˆλ‹€.

type Pick<T, K> = { [k in K]: T[k] };

type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>;

νƒœκ·Έλœ μœ λ‹ˆμ˜¨μ—μ„œλ„ λ‹€λ₯Έ ν˜•νƒœμ˜ 쀑볡이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그런데 λ‹¨μˆœνžˆ νƒœκ·Έλ₯Ό 뢙이기 μœ„ν•΄μ„œ νƒ€μž…μ„ μ‚¬μš©ν•œλ‹€λ©΄ 어떨지 생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface SaveAction {
type: 'save';
// ...
}

interface LoadAction {
type: 'load';
// ...
}

type Action = SaveAction | LoadAction;
type ActionType = 'save' | 'load'; // νƒ€μž…μ˜ 반볡!

Action μœ λ‹ˆμ˜¨μ„ μΈλ±μ‹±ν•˜λ©΄ νƒ€μž… 반볡 없이 ActionType은 μžλ™μ μœΌλ‘œ κ·Έ νƒ€μž…μ„ ν¬ν•©ν•©λ‹ˆλ‹€. ActionType은 Pick을 μ‚¬μš©ν•˜μ—¬ μ–»κ²Œ λ˜λŠ”, type 속성을 κ°€μ§€λŠ” μΈν„°νŽ˜μ΄μŠ€μ™€λŠ” λ‹€λ¦…λ‹ˆλ‹€.

type ActionRec = Pick<Action, 'type'>; // { type: "save" | "load" }

ν•œνŽΈ μƒμ„±ν•˜κ³  λ‚œ λ‹€μŒμ— μ—…λ°μ΄νŠΈκ°€ λ˜λŠ” 클래슀λ₯Ό μ •μ˜ν•œλ‹€λ©΄, update λ©”μ„œλ“œ λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ€ μƒμ„±μžμ™€ λ™μΌν•œ λ§€κ°œλ³€μˆ˜μ΄λ©΄μ„œ, νƒ€μž… λŒ€λΆ€λΆ„μ˜ 선택적 ν•„λ“œκ°€ λ©λ‹ˆλ‹€.

interface Options {
width: number;
height: number;
color: string;
label: string;
}

interface OptionsUpdate {
width?: number;
height?: number;
color?: string;
label?: string;
}

class UIWidget {
constructor(init: Options) { /* ... */ }
update(options: OptionsUpdate) { /* ... */ }
}

λ§€ν•‘λœ νƒ€μž…κ³Ό keyofλ₯Ό μ‚¬μš©ν•˜λ©΄ OptionsμœΌλ‘œλΆ€ν„° OptionsUpdateλ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

type OptionsUpdate = { [k in keyof Options]?: Options[k] };

이 νŒ¨ν„΄ μ—­μ‹œ μ•„μ£Ό 일반적이며 ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ— Partialμ΄λΌλŠ” μ΄λ¦„μœΌλ‘œ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

class UIWidget {
constructor(init: Options) { /* ... */ }
update(options: Partial<Options>) { /* ... */ }
}

κ°’μ˜ ν˜•νƒœμ— ν•΄λ‹Ήν•˜λŠ” νƒ€μž…μ„ μ •μ˜ν•˜κ³  싢을 λ•Œλ„ μžˆμŠ΅λ‹ˆλ‹€.

const INIT_OPTIONS = {
width: 640,
height: 400,
color: '#00FF00',
label: 'VGA',
};

interface Options {
width: number;
height: number;
color: string;
label: string;
}

이런 경우 typeofλ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

type Options = typeof INIT_OPTIONS;

이 μ½”λ“œλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λŸ°νƒ€μž„ μ—°μ‚°μž typeofλ₯Ό μ‚¬μš©ν•œ κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ, μ‹€μ œλ‘œλŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ‹¨κ³„μ—μ„œ μ—°μ‚°λ˜λ©° 훨씬 더 μ •ν™•ν•˜κ²Œ νƒ€μž…μ„ ν‘œν˜„ν•©λ‹ˆλ‹€. 그런데 κ°’μœΌλ‘œλΆ€ν„° νƒ€μž…μ„ λ§Œλ“€μ–΄λ‚Ό λ•ŒλŠ” μ„ μ–Έμ˜ μˆœμ„œμ— μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έλ ‡κ²Œ ν•΄μ•Ό νƒ€μž…μ΄ 더 λͺ…확해지고, μ˜ˆμƒν•˜κΈ° μ–΄λ €μš΄ νƒ€μž… 변동을 방지할 수 μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜λ‚˜ λ©”μ„œλ“œμ˜ λ°˜ν™˜ 값에 λͺ…λͺ…λœ νƒ€μž…μ„ λ§Œλ“€κ³  싢을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

function getUserInfo(userId: string) {
// ...
return {
userId,
name,
age,
height,
weight,
favoriteColor,
};
}

μ΄λ•ŒλŠ” 쑰건뢀 νƒ€μž…μ΄ ν•„μš”ν•©λ‹ˆλ‹€. ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” μ΄λŸ¬ν•œ 일반적인 νŒ¨ν„΄μ˜ μ œλ„ˆλ¦­ νƒ€μž…μ΄ μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

type UserInfo = ReturnType<typeof getUserInfo>;

μ œλ„ˆλ¦­ νƒ€μž…μ€ νƒ€μž…μ„ μœ„ν•œ ν•¨μˆ˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. 그리고 ν•¨μˆ˜μ—λŠ” μ½”λ“œμ— λŒ€ν•œ DRY 원칙을 지킬 λ•Œ μœ μš©ν•˜κ²Œ μ‚¬μš©λ©λ‹ˆλ‹€. ν•¨μˆ˜μ—μ„œ λ§€κ°œλ³€μˆ˜λ‘œ 맀핑할 수 μžˆλŠ” 값을 μ œν•œν•˜κΈ° μœ„ν•΄ νƒ€μž… μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ μ œλ„ˆλ¦­ νƒ€μž…μ—μ„œ λ§€κ°œλ³€μˆ˜λ₯Ό μ œν•œν•  수 μžˆλŠ” 방법이 ν•„μš”ν•©λ‹ˆλ‹€.

μ œλ„ˆλ¦­ νƒ€μž…μ—μ„œ λ§€κ°œλ³€μˆ˜λ₯Ό μ œν•œν•  수 μžˆλŠ” 방법은 extendsλ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. extendsλ₯Ό μ΄μš©ν•˜λ©΄ μ œλ„ˆλ¦­ λ§€κ°œλ³€μˆ˜κ°€ νŠΉμ • νƒ€μž…μ„ ν™•μž₯ν•œλ‹€κ³  μ„ ν—Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

interface Name {
first: string;
last: string;
}

type DancingDuo<T extends Name> = [T, T];

const couple1: DancingDuo<Name> = [
{ first: 'Fred', last: 'Astaire' },
{ first: 'Ginger', last: 'Rogers' }
]; // OK

const couple2: DancingDuo<{ first: string }> = [ // "Name" νƒ€μž…μ— ν•„μš”ν•œ "last" 속성이 μ—†μŠ΅λ‹ˆλ‹€.
{ first: 'Sonny' },
{ first: 'Cher' },
]

{ first: string }은 Name을 ν™•μž₯ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

점점 더 좔상적인 νƒ€μž…μ„ 닀루고 μžˆμ§€λ§Œ, μ›λž˜μ˜ λͺ©ν‘œλ₯Ό 잊으면 μ•ˆ λ©λ‹ˆλ‹€. μ›λž˜μ˜ λͺ©ν‘œλŠ” μœ νš¨ν•œ ν”„λ‘œκ·Έλž¨μ€ ν†΅κ³Όμ‹œν‚€κ³  λ¬΄νš¨ν•œ ν”„λ‘œκ·Έλž¨μ—λŠ” 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚€λŠ” κ²ƒμž…λ‹ˆλ‹€. κ°’ κ³΅κ°„μ—μ„œμ™€ λ§ˆμ°¬κ°€μ§€λ‘œ 반볡적인 μ½”λ“œλŠ” νƒ€μž… κ³΅κ°„μ—μ„œλ„ 쒋지 μ•ŠμŠ΅λ‹ˆλ‹€. λ°˜λ³΅ν•˜μ§€ μ•Šλ„λ‘ μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 15. 동적 데이터에 인덱슀 μ‹œκ·Έλ‹ˆμ²˜ μ‚¬μš©ν•˜κΈ°β€‹

μžλ°”μŠ€ν¬λ¦½νŠΈ κ°μ²΄λŠ” λ¬Έμžμ—΄ ν‚€λ₯Ό νƒ€μž…μ˜ 값에 관계없이 λ§€ν•‘ν•©λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” νƒ€μž…μ— 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λͺ…μ‹œν•˜μ—¬ μœ μ—°ν•˜κ²Œ 맀핑을 ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type Rocket = {
[property: string]: string
};

const rocket: Rocket = {
name: 'Falcon 9',
variant: 'v1.0',
thrust: '4,940 kN',
}; // 정상

[property: string]: string이 인덱슀 μ‹œκ·Έλ‹ˆμ²˜μ΄λ©°, λ‹€μŒ μ„Έ 가지 의미λ₯Ό λ‹΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

  • ν‚€μ˜ 이름: ν‚€μ˜ μœ„μΉ˜λ§Œ ν‘œμ‹œν•˜λŠ” μš©λ„μž…λ‹ˆλ‹€. νƒ€μž… μ²΄μ»€μ—μ„œλŠ” μ‚¬μš©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ¬΄μ‹œν•  수 μžˆλŠ” μ°Έκ³  정보라고 생각해도 λ©λ‹ˆλ‹€.
  • ν‚€μ˜ νƒ€μž…: stringμ΄λ‚˜ number λ˜λŠ” symbol의 쑰합이어야 ν•˜μ§€λ§Œ, 보톡은 string을 μ‚¬μš©ν•©λ‹ˆλ‹€.
  • κ°’μ˜ νƒ€μž…: μ–΄λ–€ 것이든 될 수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λ ‡κ²Œ νƒ€μž… 체크가 μˆ˜ν–‰λ˜λ©΄ λ„€ 가지 단점이 λ“œλŸ¬λ‚©λ‹ˆλ‹€.

  • 잘λͺ»λœ ν‚€λ₯Ό 포함해 λͺ¨λ“  ν‚€λ₯Ό ν—ˆμš©ν•©λ‹ˆλ‹€.
  • νŠΉμ • ν‚€κ°€ ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. {}도 μœ νš¨ν•œ Rocketμž…λ‹ˆλ‹€.
  • ν‚€λ§ˆλ‹€ λ‹€λ₯Έ νƒ€μž…μ„ κ°€μ§ˆ 수 μ—†μŠ΅λ‹ˆλ‹€.
  • νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ–Έμ–΄ μ„œλΉ„μŠ€λŠ” λ‹€μŒκ³Ό 같은 κ²½μš°μ— 도움이 λ˜μ§€ λͺ»ν•©λ‹ˆλ‹€. ν‚€λŠ” 무엇이든 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— μžλ™ μ™„μ„± κΈ°λŠ₯이 λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

인덱슀 μ‹œκ·Έλ‹ˆμ²˜λŠ” λΆ€μ •ν™•ν•˜λ―€λ‘œ 더 λ‚˜μ€ 방법을 μ°Ύμ•„μ•Ό ν•©λ‹ˆλ‹€.

interface Rocket {
name: string;
variant: string;
thrust_kN: number;
}

const falconHeavy: Rocket = {
name: 'Falcon Heavy',
variant: 'v1',
thrust_kN: 15_200,
};

인덱슀 μ‹œκ·Έλ‹ˆμ²˜λŠ” 동적 데이터λ₯Ό ν‘œν˜„ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
μ–΄λ–€ νƒ€μž…μ— κ°€λŠ₯ν•œ ν•„λ“œκ°€ μ œν•œλ˜μ–΄ μžˆλŠ” 경우라면 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ‘œ λͺ¨λΈλ§ν•˜μ§€ 말아야 ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ 데이터에 A, B, C, D 같은 ν‚€κ°€ μžˆμ§€λ§Œ, μ–Όλ§ˆλ‚˜ 많이 μžˆλŠ”μ§€ λͺ¨λ₯Έλ‹€λ©΄ 선택적 ν•„λ“œ λ˜λŠ” μœ λ‹ˆμ˜¨ νƒ€μž…μœΌλ‘œ λͺ¨λΈλ§ν•˜λ©΄ λ©λ‹ˆλ‹€.

string νƒ€μž…μ΄ λ„ˆλ¬΄ κ΄‘λ²”μœ„ν•΄μ„œ 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ₯Ό μ‚¬μš©ν•˜λŠ” 데 λ¬Έμ œκ°€ μžˆλ‹€λ©΄, 두 가지 λ‹€λ₯Έ λŒ€μ•ˆμ„ 생각해 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
첫 번째, Recordλ₯Ό μ‚¬μš©ν•˜λŠ” λ°©λ²•μ΄λ‹ˆλ‹€. RecordλŠ” ν‚€ νƒ€μž…μ— μœ μ—°μ„±μ„ μ œκ³΅ν•˜λŠ” μ œλ„ˆλ¦­ νƒ€μž…μž…λ‹ˆλ‹€. 특히, string의 λΆ€λΆ„ 집합을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type Vec3D = Record<'x' | 'y' | 'z', number>;
// Type Vec3D = {
// x: number;
// y: number;
// z: number;
// }

두 번째, λ§€ν•‘λœ νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€. λ§€ν•‘λœ νƒ€μž…μ€ ν‚€λ§ˆλ‹€ λ³„λ„μ˜ νƒ€μž…μ„ μ‚¬μš©ν•˜κ²Œ ν•΄ μ€λ‹ˆλ‹€.

type Vec3D = {
[k in 'x' | 'y' | 'z']: number
};
// Type Vec3D = {
// x: number;
// y: number;
// z: number;
// }

type ABC = {
[k in 'a' | 'b' | 'c']: k extends 'b' ? string : number
};
// Type ABC = {
// a: number;
// b: string;
// c: number;
// }

πŸ₯• μ•„μ΄ν…œ 16. number 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ³΄λ‹€λŠ” Array, νŠœν”Œ, ArrayLikeλ₯Ό μ‚¬μš©ν•˜κΈ°β€‹

μ–΄λ–€ 길이λ₯Ό κ°€μ§€λŠ” λ°°μ—΄κ³Ό λΉ„μŠ·ν•œ ν˜•νƒœμ˜ νŠœν”Œμ„ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ— μžˆλŠ” ArrayLike νƒ€μž…μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

function checkedAccess<T>(xs: ArrayLike<T>, i: number): T {
if (i < xs.length) {
return xs[i];
}

throw new Error(`λ°°μ—΄μ˜ 끝을 μ§€λ‚˜μ„œ ${i}λ₯Ό μ ‘κ·Όν•˜λ €κ³  ν–ˆμŠ΅λ‹ˆλ‹€.`);
}

이 μ˜ˆμ œλŠ” 길이와 숫자 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ§Œ μžˆμŠ΅λ‹ˆλ‹€. 이런 κ²½μš°κ°€ μ‹€μ œλ‘œλŠ” λ“œλ¬ΌκΈ°λŠ” ν•˜μ§€λ§Œ ν•„μš”ν•˜λ‹€λ©΄ ArrayLikeλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ ArrayLikeλ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ ν‚€λŠ” μ—¬μ „νžˆ λ¬Έμžμ—΄μ΄λΌλŠ” 점을 μžŠμ§€ 말아야 ν•©λ‹ˆλ‹€.

const tupleLike: ArrayLike<string> = {
'0': 'A',
'1': 'B',
length: 2,
}; // 정상

배열은 κ°μ²΄μ΄λ―€λ‘œ ν‚€λŠ” μˆ«μžκ°€ μ•„λ‹ˆλΌ λ¬Έμžμ—΄μž…λ‹ˆλ‹€. 인덱슀 μ‹œκ·Έλ‹ˆμ²˜λ‘œ μ‚¬μš©λœ number νƒ€μž…μ€ 버그λ₯Ό 작기 μœ„ν•œ 순수 νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ½”λ“œμž…λ‹ˆλ‹€.
인덱슀 μ‹œκ·Έλ‹ˆμ²˜μ— numberλ₯Ό μ‚¬μš©ν•˜κΈ°λ³΄λ‹€ Arrayλ‚˜ νŠœν”Œ, λ˜λŠ” ArrayLike νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 17. λ³€κ²½ κ΄€λ ¨λœ 였λ₯˜ 방지λ₯Ό μœ„ν•΄ readonly μ‚¬μš©ν•˜κΈ°β€‹

readonly number[]λŠ” "νƒ€μž…"이고, number[]와 κ΅¬λΆ„λ˜λŠ” λͺ‡ 가지 νŠΉμ§•μ΄ μžˆμŠ΅λ‹ˆλ‹€.

  • λ°°μ—΄μ˜ μš”μ†Œλ₯Ό 읽을 수 μžˆμ§€λ§Œ, μ“Έ μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€.
  • lengthλ₯Ό 읽을 수 μžˆμ§€λ§Œ, λ°”κΏ€ μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€.(배열을 변경함)
  • 배열을 λ³€κ²½ν•˜λŠ” pop을 λΉ„λ‘―ν•œ λ‹€λ₯Έ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 μ—†μŠ΅λ‹ˆλ‹€.

number[]λŠ” readonly number[]보닀 κΈ°λŠ₯이 많기 λ•Œλ¬Έμ—, readonly number[]의 μ„œλΈŒνƒ€μž…μ΄ λ©λ‹ˆλ‹€. λ”°λΌμ„œ λ³€κ²½ κ°€λŠ₯ν•œ 배열을 readonly 배열에 ν• λ‹Ήν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ κ·Έ λ°˜λŒ€λŠ” λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

const a: number[] = [1, 2, 3];
const b: readonly number[] = a;
const c: number[] = b; // "readonly number[]" νƒ€μž…μ€ "readonly"μ΄λ―€λ‘œ λ³€κ²½ κ°€λŠ₯ν•œ "number[]" νƒ€μž…μ—₯ 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

νƒ€μž… 단언문 없이 readonly μ ‘κ·Όμ œμ–΄μžλ₯Ό μ œκ±°ν•  수 μžˆλ‹€λ©΄ readonlyλŠ” μ“Έλͺ¨μ—†μ„ κ²ƒμ΄λ―€λ‘œ μ—¬κΈ°μ„œ 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 게 μ΄μΉ˜μ— λ§žμŠ΅λ‹ˆλ‹€.
λ§€κ°œλ³€μˆ˜λ₯Ό readonly둜 μ„ μ–Έν•˜λ©΄ λ‹€μŒκ³Ό 같은 일이 μƒκΉλ‹ˆλ‹€.

  • νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λ§€κ°œλ³€μˆ˜κ°€ ν•¨μˆ˜ λ‚΄μ—μ„œ 변경이 μΌμ–΄λ‚˜λŠ”μ§€ μ²΄ν¬ν•©λ‹ˆλ‹€.
  • ν˜ΈμΆœν•˜λŠ” μͺ½μ—μ„œλŠ” ν•¨μˆ˜κ°€ λ§€κ°œλ³€μˆ˜λ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 보μž₯을 λ°›κ²Œ λ©λ‹ˆλ‹€.
  • ν˜ΈμΆœν•˜λŠ” μͺ½μ—μ„œ ν•¨μˆ˜μ— readonly 배열을 λ§€κ°œλ³€μˆ˜λ‘œ 넣을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ”(νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλ„ λ§ˆμ°¬κ°€μ§€) λͺ…μ‹œμ μœΌλ‘œ μ–ΈκΈ‰ν•˜μ§€ μ•ŠλŠ” ν•œ, ν•¨μˆ˜κ°€ λ§€κ°œλ³€μˆ˜λ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ΄λŸ¬ν•œ 암묡적인 방법은 νƒ€μž… 체크에 문제λ₯Ό μΌμœΌν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€. λͺ…μ‹œμ μΈ 방법을 μ‚¬μš©ν•˜λŠ” 것이 μ»΄νŒŒμΌλŸ¬μ™€ μ‚¬λžŒ λͺ¨λ‘μ—κ²Œ μ’‹μŠ΅λ‹ˆλ‹€.

λ§Œμ•½ ν•¨μˆ˜κ°€ λ§€κ°œλ³€μˆ˜λ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, readonly둜 μ„ μ–Έν•΄μ•Ό ν•©λ‹ˆλ‹€. 이둜 μΈν•œ 단점을 ꡳ이 μ°Ύμ•„λ³΄μžλ©΄ λ§€κ°œλ³€μˆ˜κ°€ readonly둜 μ„ μ–Έλ˜μ§€ μ•Šμ€ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•  κ²½μš°λ„ μžˆλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. λ§Œμ•½ ν•¨μˆ˜κ°€ λ§€κ°œλ³€μˆ˜λ₯Ό λ³€κ²½ν•˜μ§€ μ•Šκ³ λ„ μ œμ–΄κ°€ κ°€λŠ₯ν•˜λ‹€λ©΄ readonly둜 μ„ μ–Έν•˜λ©΄ λ©λ‹ˆλ‹€. λ§Œμ•½ λ‹€λ₯Έ λΌμ΄λΈŒλŸ¬λ¦¬μ— μžˆλŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” 경우라면, νƒ€μž… 선언을 λ°”κΏ€ 수 μ—†μœΌλ―€λ‘œ νƒ€μž… 단언문을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. readonlyλ₯Ό μ‚¬μš©ν•˜λ©΄ 지역 λ³€μˆ˜μ™€ κ΄€λ ¨λœ λͺ¨λ“  μ’…λ₯˜μ˜ λ³€κ²½ 였λ₯˜λ₯Ό 방지할 수 μžˆμŠ΅λ‹ˆλ‹€.

readonlyλŠ” μ–•κ²Œ(shallow) λ™μž‘ν•œλ‹€λŠ” 것에 μœ μ˜ν•˜λ©° μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ§Œμ•½ 객체의 readonly 배열이 μžˆλ‹€λ©΄, κ·Έ 객체 μžμ²΄λŠ” readonlyκ°€ μ•„λ‹™λ‹ˆλ‹€.
λΉ„μŠ·ν•œ κ²½μš°κ°€ readonly의 μ‚¬μ΄Œ 격이자 객체에 μ‚¬μš©λ˜λŠ” Readonly μ œλ„ˆλ¦­μ—λ„ ν•΄λ‹Ήν•©λ‹ˆλ‹€.

interface Outer {
inner: {
x: number;
}
}

const o: Readonly<Outer> = {
inner: {
x: 0
}
};

o.inner = { x: 1 }; // 읽기 μ „μš© 속성이기 λ•Œλ¬Έμ— inner에 ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.
o.inner.x = 1; // 정상

νƒ€μž… 별칭을 λ§Œλ“  λ‹€μŒμ— μ •ν™•νžˆ 무슨 일이 μΌμ–΄λ‚˜λŠ”μ§€ νŽΈμ§‘κΈ°μ—μ„œ μ‚΄νŽ΄λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

type T = Readonly<Outer>;
// Type T = {
// readonly inner: {
// x: number;
// };
// }

μ€‘μš”ν•œ 점은 readonly μ ‘κ·Όμ œμ–΄μžλŠ” inner에 μ μš©λ˜λŠ” 것이지 xλŠ” μ•„λ‹ˆλΌλŠ” κ²ƒμž…λ‹ˆλ‹€. ν˜„μž¬ μ‹œμ μ—λŠ” κΉŠμ€(deep) readonly νƒ€μž…μ΄ 기본적으둜 μ§€μ›λ˜μ§€ μ•Šμ§€λ§Œ, μ œλ„ˆλ¦­μ„ λ§Œλ“€λ©΄ κΉŠμ€ readonly νƒ€μž…μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ œλ„ˆλ¦­μ€ λ§Œλ“€κΈ° κΉŒλ‹€λ‘­κΈ° λ•Œλ¬Έμ— 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 게 λ‚«μŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ ts-essentials에 μžˆλŠ” DeepReadonly μ œλ„ˆλ¦­μ„ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 18. λ§€ν•‘λœ νƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ 값을 λ™κΈ°ν™”ν•˜κΈ°β€‹

산점도λ₯Ό 그리기 μœ„ν•œ UI μ»΄ν¬λ„ŒνŠΈλ₯Ό μž‘μ„±ν•œλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—λŠ” λ””μŠ€ν”Œλ ˆμ΄μ™€ λ™μž‘μ„ μ œμ–΄ν•˜κΈ° μœ„ν•œ λͺ‡ 가지 λ‹€λ₯Έ νƒ€μž…μ˜ 속성이 ν¬ν•¨λ©λ‹ˆλ‹€.

interface ScatterProps {
// The data
xs: number[];
ys: number[];

// Display
xRange: [number, number];
yRange: [number, number];
color: string;

// Events
onClick: (x: number, y: number, index: number) => void;
}

λΆˆν•„μš”ν•œ μž‘μ—…μ„ ν”Όν•˜κΈ° μœ„ν•΄, ν•„μš”ν•  λ•Œλ§Œ 차트λ₯Ό λ‹€μ‹œ 그릴 수 μžˆμŠ΅λ‹ˆλ‹€. λ°μ΄ν„°λ‚˜ λ””μŠ€ν”Œλ ˆμ΄ 속성이 λ³€κ²½λ˜λ©΄ λ‹€μ‹œ κ·Έλ €μ•Ό ν•˜μ§€λ§Œ, 이벀트 ν•Έλ“€λŸ¬κ°€ λ³€κ²½λ˜λ©΄ λ‹€μ‹œ 그릴 ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€. λ Œλ”λ§ν•  λ•Œλ§ˆλ‹€ 이벀트 ν•Έλ“€λŸ¬ Prop이 μƒˆ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ‘œ μ„€μ •λ©λ‹ˆλ‹€.

μ΅œμ ν™”λ₯Ό 두 가지 λ°©λ²•μœΌλ‘œ κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. λ‹€μŒ μ˜ˆμ œλŠ” 첫 번째 λ°©λ²•μž…λ‹ˆλ‹€.

function shouldUpdate(oldProps: ScatterProps, newProps: ScatterProps) {
let k: keyof ScatterProps;

for (k in oldProps) {
if (oldProps[k] !== newProps[k]) {
if (k !== 'onClick') return true;
}
}

return false;
}

λ§Œμ•½ μƒˆλ‘œμš΄ 속성이 μΆ”κ°€λ˜λ©΄ shouldUpdate ν•¨μˆ˜λŠ” 값이 변경될 λ•Œλ§ˆλ‹€ 차트λ₯Ό λ‹€μ‹œ 그릴 κ²ƒμž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ μ²˜λ¦¬ν•˜λŠ” 것을 보수적(conservative) 접근법 λ˜λŠ” μ‹€νŒ¨μ— λ‹«νžŒ(fail close) 접근법이라고 ν•©λ‹ˆλ‹€. 이 접근법을 μ΄μš©ν•˜λ©΄ μ°¨νŠΈκ°€ μ •ν™•ν•˜μ§€λ§Œ λ„ˆλ¬΄ 자주 그렀질 κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€.

두 번째 μ΅œμ ν™” 방법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. μ‹€νŒ¨μ— μ—΄λ¦° 접근법을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

function shouldUpdate(oldProps: ScatterProps, newProps: ScatterProps) {
return (
oldProps.xs !== newProps.xs ||
oldProps.ys !== newProps.ys ||
oldProps.xRange !== newProps.xRange ||
oldProps.yRange !== newProps.yRange ||
oldProps.color !== newProps.color
// no check for onClick
)
}

이 μ½”λ“œλŠ” 차트λ₯Ό λΆˆν•„μš”ν•˜κ²Œ λ‹€μ‹œ κ·Έλ¦¬λŠ” 단점을 ν•΄κ²°ν—€μŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ‹€μ œλ‘œ 차트λ₯Ό λ‹€μ‹œ κ·Έλ €μ•Ό ν•  κ²½μš°μ— λˆ„λ½λ˜λŠ” 일이 생길 수 μžˆμŠ΅λ‹ˆλ‹€. μ•žμ„  두 가지 μ΅œμ ν™” 방법은 λͺ¨λ‘ 이상적이지 μ•ŠμŠ΅λ‹ˆλ‹€. μƒˆλ‘œμš΄ 속성이 좔가될 λ•Œ 직접 shouldUpdateλ₯Ό κ³ μΉ˜λ„λ‘ ν•˜λŠ” 게 λ‚«μŠ΅λ‹ˆλ‹€.

λ‹€μŒμ€ νƒ€μž… 체컀가 λ™μž‘ν•˜λ„λ‘ κ°œμ„ ν•œ μ½”λ“œμž…λ‹ˆλ‹€. 핡심은 λ§€ν•‘λœ νƒ€μž„κ³Ό 객체λ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

const REQUIRES_UPDATE: { [k in keyof ScatterProps]: boolean} = {
xs: true,
ys: true,
xRange: true,
yRange: true,
color: true,
onClick: false,
};

function shouldUpdate(oldProps: ScatterProps, newProps: ScatterProps) {
let k: keyof ScatterProps;

for (k in oldProps) {
if (oldProps[k] !== newProps[k] && REQUIRES_UPDATE[k]) {
return true;
}
}

return false;
}

μ—¬κΈ°μ„œ μš°λ¦¬λŠ” μ•žμ—μ„œ λ‹€λ£¨μ—ˆλ˜ μ΅œμ ν™” μ˜ˆμ œμ—μ„œμ²˜λŸΌ μ‹€νŒ¨μ— μ—΄λ¦° 방법을 선택할지, λ‹«νžŒ 방법을 선택할지 μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.
λ§€ν•‘λœ νƒ€μž…μ€ ν•œ 객체가 또 λ‹€λ₯Έ 객체와 μ •ν™•νžˆ 같은 속성을 κ°€μ§€κ²Œ ν•  λ•Œ μ΄μƒμ μž…λ‹ˆλ‹€. 이번 예제처럼 λ§€ν•‘λœ νƒ€μž…μ„ μ‚¬μš©ν•΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ½”λ“œμ— μ œμ•½μ„ κ°•μ œν•˜λ„λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.