LHS_EffectiveTypescript_08 - YDP-SPLOUNGE-CLUB/typescript-study GitHub Wiki

μ•„μ΄ν…œ 36 ν•΄λ‹Ή λΆ„μ•Όμ˜ μš©μ–΄λ‘œ νƒ€μž… 이름 μ§“κΈ°

가독성을 높이고, 좔상화 μˆ˜μ€€μ„ 올리기 μœ„ν•΄μ„œ ν•΄λ‹Ή λΆ„μ•Όμ˜ μš©μ–΄λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

νƒ€μž… 이름 μ§“κΈ° μ—­μ‹œ νƒ€μž… μ„€κ³„μ˜ μ€‘μš”ν•œ 뢀뢄이닀.

μ—„μ„ λœ νƒ€μž…, 속성, λ³€μˆ˜μ˜ 이름은 μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν•˜κ³  μ½”λ“œμ™€ νƒ€μž…μ˜ 좔상화 μˆ˜μ€€μ„ λ†’μ—¬μ€€λ‹€.

λ™λ¬Όλ“€μ˜ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό κ΅¬μΆ•ν•œλ‹€κ³  κ°€μ •

interface Animal {  
  name: string  
  endangered: boolean  
  habitat: string  
}  
  
const leopard: Animal = {  
  name: 'Snow Leopard',  
  endangered: false,  
  habitat: 'tundra',  
}

이 μ½”λ“œμ—λŠ” 4κ°€μ§€ λ¬Έμ œκ°€ μžˆλ‹€.

  • name 은 맀우 일반적인 μš©μ–΄μ΄λ‹€. λ™λ¬Όμ˜ ν•™λͺ…인지 일반적인 λͺ…칭인지 μ•Œ 수 μ—†λ‹€.
  • endangered 속성이 λ©Έμ’… μœ„κΈ°λ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄ boolean νƒ€μž…μ„ μ‚¬μš©ν•œ 것이 μ΄μƒν•˜λ‹€. 이미 λ©Έμ’…λœ 동물을 true둜 ν•΄μ•Ό ν•˜λŠ”μ§€ νŒλ‹¨ν•  수 μ—†λ‹€. 또 μ˜λ„λ₯Ό 'λ©Έμ’…μœ„κΈ° λ˜λŠ” λ©Έμ’…'으둜 μƒκ°ν•œ 것일지도 λͺ¨λ₯Έλ‹€.
  • μ„œμ‹μ§€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” habitat 속성은 λ„ˆλ¬΄ λ²”μœ„κ°€ 넓은 stringνƒ€μž… μ•„μ΄ν…œ 33 string νƒ€μž…λ³΄λ‹€ 더 ꡬ체적인 νƒ€μž… μ‚¬μš©ν•˜κΈ°μΌ 뿐만 μ•„λ‹ˆλΌ μ„œμ‹μ§€λΌλŠ” 뜻 μžμ²΄λ„ λΆˆλΆ„λͺ…ν•˜κΈ° λ•Œλ¬Έμ— λ‹€λ₯Έ 속성듀보닀도 훨씬 λͺ¨ν˜Έν•˜λ‹€.
  • 객체의 λ³€μˆ˜λͺ…이 leopard μ΄μ§€λ§Œ name μ†μ„±μ˜ 값은 'Snow Leopard' 이닀. 객체의 이름과 μ†μ„±μ˜ name이 λ‹€λ₯Έ μ˜λ„λ‘œ μ‚¬μš©λœ 것인지 λΆˆλΆ„λͺ…ν•˜λ‹€.

이 예제λ₯Ό ν•΄κ²°ν•˜λ €λ©΄ μž‘μ„±μžλ₯Ό μ°Ύμ•„ μ˜λ„λ₯Ό λ¬Όμ–΄μ•Ό ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μž‘μ„±μžκ°€ νšŒμ‚¬μ— μ—†κ±°λ‚˜ μ½”λ“œλ₯Ό κΈ°μ–΅ν•˜μ§€ λͺ»ν•  것이닀. λ˜λŠ” μž‘μ„±μžκ°€ λ³ΈμΈμ΄λΌλŠ” 것을 μ•Œκ²Œλ˜λŠ” μ΅œμ•…μ˜ 상황이 올 수 μžˆλ‹€....

λ‹€μŒ νƒ€μž… 선언은 μ˜λ―Έκ°€ λΆ„λͺ…ν•˜λ‹€.

interface Animal {  
    commonName: string  
    genus: string  
    species: string  
    status: ConservationStatus  
    climates: KoppenClimate[]  
}  
  
type ConservationStatus = 'EX' | 'EW' | 'CR' | 'EN' | 'VU' | 'NT' | 'LC'  
type KoppenClimate =  
    | 'Af'  | 'Am'  | 'As'  | 'Aw'  | 'BSh'  | 'BSk'  | 'BWh'  | 'BWk'  
    | 'Cfa' | 'Cfb' | 'Cfc' | 'Csa' | 'Csb'  | 'Csc'  | 'Cwa'  | 'Cwb'  
    | 'Cwc' | 'Dfa' | 'Dfb' | 'Dfc' | 'Dfd'  | 'Dsa'  | 'Dsb'  | 'Dsc'  
    | 'Dwa' | 'Dwb' | 'Dwc' | 'Dwd' | 'EF'   | 'ET'  

const snowLeopard: Animal = {  
    commonName: 'Snow Leopard',  
    genus: 'Panthera',  
    species: 'Uncia',  
    status: 'VU', // μ·¨μ•½μ’…(vulnerable)
    climates: ['ET', 'EF', 'Dfd'], // κ³ μ‚°λŒ€(alpine) or μ•„κ³ μ‚°λŒ€(subalpine)
}

λ‹€μŒ 3κ°€μ§€λ₯Ό κ°œμ„ ν–ˆλ‹€.

  • name은 commonName, genus, species λ“± ꡬ체적인 μš©μ–΄λ‘œ λŒ€μ²΄ν–ˆλ‹€.
  • endangerdλŠ” 동물 보호 등급에 λŒ€ν•œ IUCN의 ν‘œμ€€ λΆ„λ₯˜ 체계인 ConservationStatus νƒ€μž…μ˜ status둜 λ³€κ²½λ˜μ—ˆλ‹€.
  • habitat은 κΈ°ν›„λ₯Ό λœ»ν•˜λŠ” climateds둜 λ³€κ²½λ˜μ—ˆμœΌλ©°, 쾨펜 κΈ°ν›„ λΆ„λ₯˜λ₯Ό μ‚¬μš©ν•œλ‹€.

λ³€κ²½λœ μ½”λ“œλŠ” 데이터λ₯Ό 훨씬 λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•˜κ³  μžˆλ‹€. 그리고 정보λ₯Ό μ°ΎκΈ° μœ„ν•΄ μ‚¬λžŒμ— μ˜μ‘΄ν•  ν•„μš”κ°€ μ—†λ‹€.

같은 μ˜λ―Έμ— λ‹€λ₯Έ 이름을 뢙이면 μ•ˆλœλ‹€. νŠΉλ³„ν•œ μ˜λ―Έκ°€ μžˆμ„λ•Œλ§Œ μš©μ–΄λ₯Ό ꡬ뢄해야 ν•œλ‹€.

μ½”λ“œλ‘œ ν‘œν˜„ν•˜κ³ μž ν•˜λŠ” λͺ¨λ‘” λ·΄μ•Όμ—λŠ” 주제λ₯Ό μ„€λͺ…ν•˜κΈ° μœ„ν•œ μ „λ¬Έ μš©μ–΄λ“€μ΄ μžˆλ‹€.

자체적으둜 μš©μ–΄λ₯Ό λ§Œλ“€μ§€λ§κ³  이미 μ‘΄μž¬ν•˜λŠ” μš©μ–΄λ₯Ό μ‚¬μš©ν•˜μž. 이런 μš©μ–΄λ“€μ€ μˆ˜λ…„, μˆ˜μ‹­λ…„, 수 세기에 걸쳐 닀듬어져 μ™”μœΌλ©° ν˜„μž₯μ—μ„œ μ‹€μ œλ‘œ μ‚¬μš©λ˜κ³  μžˆμ„ 것이닀.

μ•„μ΄ν…œ 37 곡식 λͺ…μΉ­μ—λŠ” μƒν‘œλ₯Ό 뢙이기

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ꡬ쑰적 타이핑(덕 타이핑)을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ—, 값을 μ„Έλ°€ν•˜κ²Œ κ΅¬λΆ„ν•˜μ§€ λͺ»ν•˜λŠ” κ²½μš°κ°€ μžˆλ‹€. 값을 κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄ 곡식 λͺ…칭이 ν•„μš”ν•˜λ‹€λ©΄ μƒν‘œλ₯Ό λΆ™μ΄λŠ” 것을 κ³ λ €ν•΄μ•Ό ν•œλ‹€.

interface Vector2D {  
  x: number  
  y: number  
}  
function calculateNorm(p: Vector2D) {  
  return Math.sqrt(p.x * p.x + p.y * p.y)  
}  
  
calculateNorm({ x: 3, y: 4 }) // OK, result is 5  
const vec3D = { x: 3, y: 4, z: 1 }  
calculateNorm(vec3D) // OK! result is also 5

이 μ½”λ“œλŠ” ꡬ쑰적 타이핑 κ΄€μ μ—μ„œλŠ” λ¬Έμ œκ°€ μ—†λ‹€. ν•˜μ§€λ§Œ μˆ˜ν•™μ μœΌλ‘œ λ”°μ§€λ©΄ 2차원 벑터λ₯Ό μ‚¬μš©ν•΄μ•Ό μ΄μΉ˜μ— λ§žλ‹€.

ν•¨μˆ˜κ°€ 3차원 벑터λ₯Ό ν—ˆμš©ν•˜μ§€ μ•Šκ²Œ ν•˜λ €λ©΄ 곡식 λͺ…칭을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.

곡식 λͺ…μΉ­ κ°œλ…μ„ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ 흉내 λ‚΄λ €λ©΄ 'μƒν‘œ(brand)'λ₯Ό 뢙이면 λœλ‹€.

interface Vector2D {  
  _brand: '2d'  
  x: number  
  y: number  
}  
function vec2D(x: number, y: number): Vector2D {  
  return { x, y, _brand: '2d' }  
}  
function calculateNorm(p: Vector2D) {  
  return Math.sqrt(p.x * p.x + p.y * p.y) // Same as before  
}  
  
calculateNorm(vec2D(3, 4)) // OK, returns 5  
const vec3D = { x: 3, y: 4, z: 1 }  
calculateNorm(vec3D)  
// ~~~~~ Property '_brand' is missing in type...

μƒν‘œ 기법은 νƒ€μž…μ‹œμŠ€ν…œμ—μ„œ λ™μž‘ν•˜μ§€λ§Œ λŸ°νƒ€μž„μ— μƒν‘œλ₯Ό κ²€μ‚¬ν•˜λŠ” 것과 λ™μΌν•œ 효과λ₯Ό 얻을 수 μžˆλ‹€.

예λ₯Ό λ“€μ–΄, μ ˆλŒ€ 경둜λ₯Ό μ‚¬μš©ν•΄ 파일 μ‹œμŠ€ν…œμ— μ ‘κ·Όν•˜λŠ” ν•¨μˆ˜λ₯Ό κ°€μ •ν•΄λ³΄μž. λŸ°νƒ€μž„μ—λŠ” μ ˆλŒ€ 경둜('/')둜 μ‹œμž‘ν•˜λŠ”μ§€ μ²΄ν¬ν•˜κΈ° μ‰½μ§€λ§Œ, νƒ€μž…μ‹œμŠ€ν…œμ—μ„œλŠ” μ ˆλŒ€ 경둜λ₯Ό νŒλ‹¨ν•˜κΈ° μ–΄λ ΅κΈ° λ•Œλ¬Έμ— μƒν‘œ 기법을 μ‚¬μš©ν•œλ‹€.

type AbsolutePath = string & { _brand: 'abs' }  
function listAbsolutePath(path: AbsolutePath) {  
  // ...  
}  
function isAbsolutePath(path: string): path is AbsolutePath {  
  return path.startsWith('/')  
}

string νƒ€μž…μ΄λ©΄μ„œ _brand 속성을 κ°€μ§€λŠ” 객체λ₯Ό λ§Œλ“€ μˆ˜λŠ” μ—†λ‹€. AbsolutePathλŠ” μ˜¨μ „νžˆ νƒ€μž… μ‹œμŠ€ν…œμ˜ μ˜μ—­μ΄λ‹€.

λ§Œμ•½ path 값이 μ ˆλŒ€ κ²½λ‘œμ™€ μƒλŒ€ 경둜 λ‘˜ λ‹€ 될 수 μžˆλ‹€λ©΄, νƒ€μž…μ„ μ •μ œν•΄ μ£ΌλŠ” νƒ€μž… κ°€λ“œλ₯Ό μ‚¬μš©ν•΄μ„œ 였λ₯˜λ₯Ό λ°©μ§€ν•  수 μžˆλ‹€.

function f(path: string) {  
  if (isAbsolutePath(path)) {  
    listAbsolutePath(path)  
  }  listAbsolutePath(path)  
  // ~~~~ Argument of type 'string' is not assignable  
  //      to parameter of type 'AbsolutePath'}

μ•„μ΄ν…œ 38 any νƒ€μž…μ€ κ°€λŠ₯ν•œ ν•œ 쒁은 λ²”μœ„μ—μ„œλ§Œ μ‚¬μš©ν•˜κΈ°

μ˜λ„μΉ˜ μ•Šμ€ νƒ€μž… μ•ˆμ „μ„±μ˜ 손싀을 ν”Όν•˜κΈ° μœ„ν•΄μ„œ any의 μ‚¬μš© λ²”μœ„λ₯Ό μ΅œμ†Œν•œμœΌλ‘œ μ’ν˜€μ•Ό ν•œλ‹€.

function f1() {  
  const x: any = expressionReturningFoo() // μ΄λ ‡κ²Œ ν•˜μ§€ 말자.
  processBar(x)  
}  
  
function f2() {  
  const x = expressionReturningFoo()  
  processBar(x as any) // 이게 λ‚«λ‹€.
}

f2 ν•¨μˆ˜μ˜ ν˜•νƒœκ°€ ꢌμž₯λ˜λŠ” μ΄μœ λŠ” processBar ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜μ—λ§Œ μ‚¬μš©λœ ν‘œν˜„μ‹μ΄λ―€λ‘œ λ‹€λ₯Έ μ½”λ“œμ—λŠ” 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€

ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…μ΄ any인 경우 νƒ€μž… μ•ˆμ •μ„±μ΄ λ‚˜λΉ μ§„λ‹€. λ”°λΌμ„œ anyνƒ€μž…μ„ λ°˜ν™˜ν•˜λ©΄ μ ˆλŒ€ μ•ˆλœλ‹€.

function f1() {  
  const x: any = expressionReturningFoo()  
  processBar(x)  
  return x  
}  
  
function g() {  
  const foo = f1() // Type is any  
  foo.fooMethod() // 이 ν•¨μˆ˜ ν˜ΈμΆœμ€ μ²΄ν¬λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€
}

λ°˜ν™˜νƒ€μž…μ„ any둜 λ°˜ν™˜ν•˜λ©΄ κ·Έ 영ν–₯λ ₯은 ν”„λ‘œμ νŠΈ μ „λ°˜μ— μ „μ—Όλ³‘μ²˜λŸΌ νΌμ§€κ²Œλœλ‹€.

κ°•μ œλ‘œ νƒ€μž… 였λ₯˜λ₯Ό μ œκ±°ν•˜λ €λ©΄ any λŒ€μ‹  @ts-ignore μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

function f1() {  
  const x = expressionReturningFoo()  
  // @ts-ignore  
  processBar(x)  
  return x  
}

@ts-ignoreλ₯Ό μ‚¬μš©ν•œ λ‹€μŒ μ€„μ˜ 였λ₯˜κ°€ λ¬΄μ‹œλœλ‹€. ν•˜μ§€λ§Œ 근본적인 원인을 ν•΄κ²°ν•œ 것이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— λ‹€λ₯Έ κ³³μ—μ„œ 더 큰 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€.

μ•„μ΄ν…œ 39 anyλ₯Ό ꡬ체적으둜 λ³€ν˜•ν•΄μ„œ μ‚¬μš©ν•˜κΈ°

anyλ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” μ •λ§λ‘œ λͺ¨λ“  값이 ν—ˆμš©λ˜μ–΄μ•Όλ§Œ ν•˜λŠ”μ§€ λ©΄λ°€νžˆ κ²€ν† ν•΄μ•Ό ν•œλ‹€.

예λ₯Ό λ“€μ–΄ any νƒ€μž…μ˜ 값을 κ·ΈλŒ€λ‘œ μ •κ·œμ‹μ΄λ‚˜ ν•¨μˆ˜μ— λ„£λŠ” 것은 ꢌμž₯λ˜μ§€ μ•ŠλŠ”λ‹€.

function getLengthBad(array: any) {  
  // μ΄λ ‡κ²Œ ν•˜μ§€ λ§™μ‹œλ‹€!
  return array.length  
}  
  
function getLength(array: any[]) {  
  return array.length  
}

μ•žμ˜ μ˜ˆμ œμ—μ„œ anyλ₯Ό μ‚¬μš©ν•˜λŠ” getLengthBadλ³΄λ‹€λŠ” any[]λ₯Ό μ‚¬μš©ν•˜λŠ” getLengthκ°€ 더 쒋은 ν•¨μˆ˜μ΄λ‹€

μ΄μœ λŠ” μ„Έκ°€μ§€λ‹€.

  • ν•¨μˆ˜ λ‚΄μ˜ array.length νƒ€μž…μ΄ μ²΄ν¬λœλ‹€.
  • ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…μ΄ any λŒ€μ‹  number둜 μΆ”λ‘ λœλ‹€.
  • ν•¨μˆ˜ 호좜될 λ•Œ λ§€κ°œλ³€μˆ˜κ°€ 배열인지 μ²΄ν¬λœλ‹€.

인수λ₯Ό λ„£μ–΄ 싀행해보면 차이점을 μ•Œ 수 μžˆλ‹€.

any보닀 더 μ •ν™•ν•˜κ²Œ λͺ¨λΈλ§ν•  수 μžˆλ„λ‘ any[] λ˜λŠ” {[id: string]: any} λ˜λŠ” () => any 처럼 ꡬ체적인 ν˜•νƒœλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜κ°€ 객체이긴 ν•˜μ§€λ§Œ 값을 μ•Œ 수 μ—†λ‹€λ©΄ {[key: string]: any} 처럼 μ„ μ–Έν•˜λ©΄ λœλ‹€.

function hasTwelveLetterKey(o: { [key: string]: any }) {  
  for (const key in o) {  
    if (key.length === 12) {  
      return true  
    }  
  }  
  return false  
}

μœ„ 예제처럼 λ§€κ°œλ³€μˆ˜κ°€ κ°μ²΄μ§€λ§Œ 값을 μ•Œ 수 μ—†λ‹€λ©΄ ${[key: string]: any} λŒ€μ‹  λͺ¨λ“  λΉ„κΈ°λ³Έν˜• νƒ€μž…μ„ ν¬ν•¨ν•˜λŠ” object νƒ€μž…μ„ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€.

ν•¨μˆ˜μ˜ νƒ€μž…μ—λ„ λ‹¨μˆœνžˆ anyλ₯Ό μ‚¬μš©ν•΄μ„œλŠ” μ•ˆλœλ‹€. μ΅œμ†Œν•œμœΌλ‘œλ‚˜λ§ˆ ꡬ체화할 수 μžˆλŠ” μ„Έ κ°€μ§€ 방법이 μžˆλ‹€.

type Fn0 = () => any // λ§€κ°œλ³€μˆ˜ 없이 호좜 κ°€λŠ₯ν•œ λͺ¨λ“  ν•¨μˆ˜
type Fn1 = (arg: any) => any // λ§€κ°œλ³€μˆ˜ ν•œκ°œ
type FnN = (...args: any[]) => any // λͺ¨λ“  개수의 λ§€κ°œλ³€μˆ˜ "Function" νƒ€μž…κ³Ό 동일

μ•žμ˜ μ˜ˆμ œμ— λ“±μž₯ν•œ μ„Έ κ°€μ§€ ν•¨μˆ˜ νƒ€μž…μ€ λͺ¨λ‘ any λ³΄λ‹€λŠ” ꡬ체적이닀. λ§ˆμ§€λ§‰ 쀄을 잘 μ‚΄νŽ΄λ³΄λ©΄ ...args의 νƒ€μž…μ„ any[]둜 μ„ μ–Έν–ˆλ‹€. any 둜 선언해도 λ™μž‘ν•˜μ§€λ§Œ any[]둜 μ„ μ–Έν•˜λ©΄ λ°°μ—΄ ν˜•νƒœλΌλŠ” 것을 μ•Œ 수 μžˆμ–΄ 더 ꡬ체적이닀.

μ•„μ΄ν…œ 40 ν•¨μˆ˜ μ•ˆμœΌλ‘œ νƒ€μž… 단언문 감좔기

νƒ€μž… 선언문은 일반적으둜 νƒ€μž…μ„ μœ„ν—˜ν•˜κ²Œ λ§Œλ“€μ§€λ§Œ 상황에 따라 ν•„μš”ν•˜κΈ°λ„ ν•˜κ³  ν˜„μ‹€μ μΈ 해결책이 λ˜κΈ°λ„ ν•œλ‹€. λΆˆκ°€ν”Όν•˜κ²Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€λ©΄, μ •ν™•ν•œ μ •μ˜λ₯Ό κ°€μ§€λŠ” ν•¨μˆ˜ μ•ˆμœΌλ‘œ μˆ¨κΈ°λ„λ‘ ν•˜μž.

ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜λ‹€ 보면, μ™ΈλΆ€λ‘œ λ“œλŸ¬λ‚œ νƒ€μž… μ •μ˜λŠ” κ°„λ‹¨ν•˜μ§€λ§Œ λ‚΄λΆ€ 둜직이 λ³΅μž‘ν•΄μ„œ μ•ˆμ „ν•œ νƒ€μž…μœΌλ‘œ κ΅¬ν˜„ν•˜κΈ° μ–΄λ €μš΄ κ²½μš°κ°€ λ§Žλ‹€.

ν•¨μˆ˜μ˜ λͺ¨λ‘” 뢀뢄을 μ•ˆμ „ν•œ νƒ€μž…μœΌλ‘œ κ΅¬ν˜„ν•˜λŠ” 것이 μ΄μƒμ μ΄μ§€λ§Œ, λΆˆν•„μš”ν•œ μ˜ˆμ™Έ μƒν™©κΉŒμ§€ κ³ λ €ν•΄ κ°€λ©° νƒ€μž… 정보λ₯Ό νž˜λ“€κ²Œ ꡬ성할 ν•„μš”λŠ” μ—†λ‹€.

ν•¨μˆ˜ λ‚΄λΆ€μ—λŠ” νƒ€μž… 단언을 μ‚¬μš©ν•˜κ³  ν•¨μˆ˜ μ™ΈλΆ€λ‘œ λ“œλŸ¬λ‚˜λŠ” νƒ€μž… μ •μ˜λ₯Ό μ •ν™•νžˆ λͺ…μ‹œν•˜λŠ” μ •λ„λ‘œ λλ‚΄λŠ” 것이 λ‚«λ‹€.

ν”„λ‘œμ νŠΈ μ „λ°˜μ— μœ„ν—˜ν•œ νƒ€μž… 단언문이 λ“œλŸ¬λ‚˜μžˆλŠ” 것보닀, μ œλŒ€λ‘œ νƒ€μž…μ΄ μ •μ˜λœ ν•¨μˆ˜ μ•ˆμœΌλ‘œ νƒ€μž… 단언문을 κ°μΆ”λŠ” 것이 더 쒋은 섀계이닀.

declare function shallowEqual(a: any, b: any): boolean  
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {  
  for (const [k, aVal] of Object.entries(a)) {  
    if (!(k in b) || aVal !== b[k]) {  
      // ~~~~ Element implicitly has an 'any' type  
      //      because type '{}' has no index signature      return false  
    }  
  }  
  return Object.keys(a).length === Object.keys(b).length  
}

in ꡬ문의 k in b 체크둜 b 객체에 k 속성이 μžˆλ‹€λŠ” 것을 ν™•μΈν–ˆμ§€λ§Œ b[k] λΆ€λΆ„μ—μ„œ 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 것이 μ΄μƒν•˜λ‹€.

어쨋든 μ‹€μ œ 였λ₯˜κ°€ μ•„λ‹ˆλΌλŠ” 것을 μ•Œκ³  있기 λ•Œλ¬Έμ— any둜 λ‹¨μ–Έν•˜λŠ” 수 밖에 μ—†λ‹€.

declare function shallowEqual(a: any, b: any): boolean  
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {  
  for (const [k, aVal] of Object.entries(a)) {  
    if (!(k in b) || aVal !== (b as any)[k]) {  
      return false  
    }  
  }  
  return Object.keys(a).length === Object.keys(b).length  
}

b as any νƒ€μž… 단언문은 μ•ˆμ „ν•˜λ©° (k in b 체크λ₯Ό ν–ˆμœΌλ―€λ‘œ), κ²°κ΅­ μ •ν™•ν•œ νƒ€μž…μœΌλ‘œ μ •μ˜λ˜κ³  μ œλŒ€λ‘œ κ΅¬ν˜„λœ ν•¨μˆ˜κ°€ λœλ‹€. 객체가 같은지 μ²΄ν¬ν•˜κΈ° μœ„ν•΄ 객체 μˆœνšŒμ™€ 단언문이 μ½”λ“œμ— 직접 λ“€μ–΄κ°€λŠ” 것보닀, μ•žμ˜ μ½”λ“œμ²˜λŸΌ λ³„λ„μ˜ ν•¨μˆ˜λ‘œ 뢄리해 λ‚΄λŠ” 것이 훨씬 쒋은 섀계이닀.