πŸ‘¨β€πŸ’»

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ ꡬ쑰적 타이핑

μ‹€λ¬΄μ—μ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό 1λ…„ 정도 닀루며 더 κΉŠμ€ μ΄ν•΄μ˜ ν•„μš”μ„±μ„ 느끼던 참에 'μ΄νŽ™ν‹°λΈŒ νƒ€μž…μŠ€ν¬λ¦½νŠΈ'λΌλŠ” 책을 읽게 됐고 μ‹€μš©μ μΈ λ‚΄μš©μ΄ λ§Žμ•„ 정리해두렀고 ν•©λ‹ˆλ‹€.

μ±…μ—λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ νŠΉμ„± 쀑 ν•˜λ‚˜μΈ 'ꡬ쑰적 타이핑'μ΄λΌλŠ” κ°œλ…μ΄ 자주 λ“±μž₯ν•©λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ 덕 타이핑1 (duck typing) 기반이고 νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 이λ₯Ό λͺ¨λΈλ§ν•˜κΈ° μœ„ν•΄ ꡬ쑰적 타이핑을 μ‚¬μš©ν•¨μ„ 이해해야 ν•©λ‹ˆλ‹€. νƒ€μž…μ€ '봉인'λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

β€” μ•„μ΄ν…œ 4. ꡬ쑰적 타이핑에 μ΅μˆ™ν•΄μ§€κΈ°

ꡬ쑰적 νƒ€μ΄ν•‘μ˜ κ°€μž₯ 기본적인 νŠΉμ„±μ€ 값을 ν• λ‹Ήν•  λ•Œ μ •μ˜λœ νƒ€μž…μ— ν•„μš”ν•œ 속성을 가지고 μžˆλ‹€λ©΄ ν˜Έν™˜λœλ‹€λŠ” μ μž…λ‹ˆλ‹€.

type Person = { name: string }; let person: Person; const employee = { name: 'Sanghyeon', job: 'Developer', }; person = employee; // OK

employeeλŠ” Person νƒ€μž…μ— ν•„μš”ν•œ name 속성을 가지고 있기 λ•Œλ¬Έμ— κ·Έ μ™Έμ˜ 속성이 μžˆλ”λΌλ„ person의 κ°’μœΌλ‘œ ν• λ‹Ήν•  수 μžˆμŠ΅λ‹ˆλ‹€. ('μž‰μ—¬ 속성 체크'λΌλŠ” μ˜ˆμ™Έκ°€ 있고, μ•„λž˜μ—μ„œ μ‚΄νŽ΄λ³Ό μ˜ˆμ •μž…λ‹ˆλ‹€)

νƒ€μž…μ€ κ°’μ˜ 집합

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 두 νƒ€μž…μ„ μ–΄λ–»κ²Œ λΉ„κ΅ν•˜κ³  ν• λ‹Ή κ°€λŠ₯ μ—¬λΆ€λ₯Ό νŒλ‹¨ν• κΉŒμš”?

νƒ€μž…μ„ κ°’μ˜ μ§‘ν•©μœΌλ‘œ μƒκ°ν•˜λ©΄ μ΄ν•΄ν•˜κΈ° νŽΈν•©λ‹ˆλ‹€(νƒ€μž…μ˜ 'λ²”μœ„').

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

νƒ€μž…μ„ κ°’μ˜ μ§‘ν•©μœΌλ‘œ λ³Έλ‹€λ©΄ numberλŠ” 1, 2, ..., n κ³Ό 같은 λ¬΄ν•œ 집합이 되고, boolean은 true, falseλ₯Ό κ°’μœΌλ‘œ κ°–λŠ” μœ ν•œ 집합이 λ©λ‹ˆλ‹€. μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…μ˜ 합은 union(|)으둜 μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그리고 κ²ΉμΉ˜λŠ” νƒ€μž…μ€ intersection(&)으둜 μ •μ˜λ©λ‹ˆλ‹€. κ²ΉμΉ˜λŠ” νƒ€μž…μ΄ μ—†λŠ” 경우 곡집합이 λ©λ‹ˆλ‹€.

type AB = 'a' | 'b'; type BC = 'b' | 'c'; type CD = 'c' | 'd'; type ABC = AB | BC; // 'a' | 'b' | 'c' type B = AB & BC; // 'b' type EMPTY = AB & CD; // never

νƒ€μž…μ„ 집합에 λŒ€μ‘ν•΄λ³΄λ©΄ λ‹€μŒ ν‘œμ™€ κ°™μŠ΅λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈ μš©μ–΄μ§‘ν•© μš©μ–΄
never곡집합
1, a λ“±μ˜ λ¦¬ν„°λŸ΄μ›μ†Œκ°€ 1개인 집합
T1이 T2에 ν• λ‹Ή κ°€λŠ₯T1이 T2의 λΆ€λΆ„ 집합
T1 | T2 (Union)T1κ³Ό T2의 합집합
T1 & T2 (Intersection)T1κ³Ό T2의 ꡐ집합

μœ„μ˜ ν‘œμ— λ”°λ₯΄λ©΄ μ–΄λ–€ νƒ€μž… T1이 T2에 ν• λ‹Ή κ°€λŠ₯ν•˜λ €λ©΄ T1이 T2의 λΆ€λΆ„ 집합이어야 ν•©λ‹ˆλ‹€.

  • 'a'λΌλŠ” 값은 'a' | 'b' νƒ€μž…μ˜ λ²”μœ„μ— ν¬ν•¨λ©λ‹ˆλ‹€. λ”°λΌμ„œ ν• λ‹Ή κ°€λŠ₯ν•©λ‹ˆλ‹€.
  • [1, 2]λΌλŠ” 값은 number[] νƒ€μž…μ˜ λ²”μœ„μ— ν¬ν•¨λ©λ‹ˆλ‹€. λ”°λΌμ„œ ν• λ‹Ή κ°€λŠ₯ν•©λ‹ˆλ‹€.

μ—¬κΈ°κΉŒμ§„ μ°Έ μ‰½μŠ΅λ‹ˆλ‹€. 그런데 μ•žμ„œ λ‚˜μ˜¨ Person 객체의 μ˜ˆμ œμ— λŒ€μž…ν•˜λ©΄ 점점 머리가 λ³΅μž‘ν•΄μ§‘λ‹ˆλ‹€.

nameκ³Ό job 속성을 가진 employeeλŠ” name μ†μ„±λ§Œ 가진 Person에 ν• λ‹Ή κ°€λŠ₯ν•©λ‹ˆλ‹€. 즉, employee νƒ€μž…μ€ Person의 λΆ€λΆ„ μ§‘ν•©μ΄λΌλŠ” μ˜λ―Έκ°€ λ©λ‹ˆλ‹€. μ €λŠ” 이 뢀뢄이 μ°Έ 이해가 λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. μ™œ μΆ”κ°€ 속성을 가진 객체가 λΆ€λΆ„ 집합이 λ˜λŠ”κ±΄μ§€?

μ‹€μ œλ‘œ 객체 νƒ€μž…μ˜ μ—°μ‚°κ³Ό 좔둠은 μ•„λž˜μ™€ 같이 λ™μž‘ν•©λ‹ˆλ‹€.

type Person = { name: string; }; type Employee = { job: string; }; function union(input: Person | Employee) { input.name // ~~~~ Property 'name' does not exist on type 'Person | Employee'. input.job // ~~~~ Property 'job' does not exist on type 'Person | Employee'. } function intersection(input: Person & Employee) { input.name // string input.job // string }

μ €λŠ” μœ„ 문법을 μ ‘ν–ˆμ„ λ•Œ 속성을 κΈ°μ€€μœΌλ‘œ μ •λ°˜λŒ€μ˜ κ²°κ³Όλ₯Ό μ˜ˆμƒν–ˆμŠ΅λ‹ˆλ‹€.

νƒ€μž… μ—°μ‚°μžλŠ” μΈν„°νŽ˜μ΄μŠ€μ˜ 속성이 μ•„λ‹Œ, κ°’μ˜ 집합(νƒ€μž…μ˜ λ²”μœ„)에 μ μš©λ©λ‹ˆλ‹€. 그리고 좔가적인 속성을 κ°€μ§€λŠ” 값도 μ—¬μ „νžˆ κ·Έ νƒ€μž…μ— μ†ν•©λ‹ˆλ‹€.

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

μ±…μ—μ„œλŠ” μœ„μ™€ 같이 μ„€λͺ…λ˜μ–΄ μžˆμ–΄, 제 κ°œλ…μ„ λ°”λ‘œ 작기 μœ„ν•΄ κ°’μ˜ μ§‘ν•©μœΌλ‘œ νƒ€μž… μ—°μ‚°ν•˜λŠ” 방법을 κ³ λ―Όν•΄λ΄€μŠ΅λ‹ˆλ‹€.

Person νƒ€μž…μ— ν• λ‹Ή κ°€λŠ₯ν•œ κ°’μ˜ 집합:

{ name: 'Sanghyeon' } { name: 'Sanghyeon', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μ™Έ `name` ν•„λ“œλ₯Ό 가진 객체

Employee νƒ€μž…μ— ν• λ‹Ή κ°€λŠ₯ν•œ κ°’μ˜ 집합:

{ job: 'Developer' } { job: 'Developer', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μ™Έ `job` ν•„λ“œλ₯Ό 가진 객체

Person | Employee (합집합):

μœ λ‹ˆμ˜¨ μ—°μ‚°μœΌλ‘œ κ°’μ˜ 집합을 λͺ¨λ‘ λͺ¨μ•„보면 항상 μ‘΄μž¬ν•˜λŠ” ν•„λ“œκ°€ μ—†λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

{ name: 'Sanghyeon' } { name: 'Sanghyeon', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } { job: 'Developer' } { job: 'Developer', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μ™Έ `name` ν•„λ“œλ₯Ό 가진 객체 κ·Έ μ™Έ `job` ν•„λ“œλ₯Ό 가진 객체

Person & Employee (ꡐ집합):

μΈν„°μ„Ήμ…˜ μ—°μ‚°μœΌλ‘œ κ³΅ν†΅λœ κ°’μ˜ 집합을 좔렀보면 name, job ν•„λ“œκ°€ 항상 μ‘΄μž¬ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

{ name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' }

μœ„μ™€ 같이 μ μ–΄λ³΄λ‹ˆ λ­”κ°€ 이해가 될듯 말듯 ν•©λ‹ˆλ‹€. μˆ˜μ‹μœΌλ‘œλ„ ν•œλ²ˆ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 잊고 μ§€λ‚΄λ˜ λ“œλͺ¨λ₯΄κ°„ 법칙2을 μ μš©ν•΄ μ•„λž˜μ™€ 같이 써 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

keyof (Person | Employee) = (keyof Person) & (keyof Employee) = 'name' & 'job' // never (곡집합) keyof (Person & Employee) = (keyof Person) | (keyof Employee) = 'name' | 'job' // 'name' | 'job'

(책에 λ‚˜μ˜¨ λ‚΄μš©μ΄κΈ΄ ν•˜μ§€λ§Œ) λ­”κ°€λ₯Ό 증λͺ…ν•΄λ‚Έ κΈ°λΆ„μž…λ‹ˆλ‹€! 이제 객체 νƒ€μž… 연산은 μ–΄λŠ 정도 이해가 λμŠ΅λ‹ˆλ‹€.

ꡬ쑰적 타이핑 κ΄€μ μ˜ extends

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ extendsλŠ” μΈν„°νŽ˜μ΄μŠ€μ˜ ν™•μž₯ λ˜λŠ” μ œλ„€λ¦­μ˜ ν•œμ •μžλ‘œ μ“°μž…λ‹ˆλ‹€. extends λ™μž‘μ„ μ΄ν•΄ν•˜κΈ° μœ„ν•΄ μ•„λž˜μ™€ 같이 μΈν„°νŽ˜μ΄μŠ€μ™€ ν™•μž₯된 μΈν„°νŽ˜μ΄μŠ€ κ°„μ˜ 관계λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface Person { name: string; } interface Employee extends Person { job: string; } const person1: Person = { name: 'Sanghyeon' }; const employee1: Employee = { name: 'Sanghyeon', job: 'Developer' }; let person2: Person; let employee2: Employee; person2 = employee1; // OK employee2 = person1; // ~~~ Property 'job' is missing in type 'Person' but required in type 'Employee'.

Person은 Employee에 ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€. 반면 EmployeeλŠ” Person에 ν• λ‹Ή κ°€λŠ₯ν•©λ‹ˆλ‹€. μ•žμ„œ λ‚˜μ˜¨ 집합에 λŒ€μ‘λœ ν‘œμ— λ”°λ₯΄λ©΄ Employeeλ₯Ό Person에 ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€λŠ” 것은 Employeeκ°€ Person의 λΆ€λΆ„ 집합이 λœλ‹€λŠ” 것을 λœ»ν•©λ‹ˆλ‹€.

이제 A extends Bλ₯Ό 보면 AλŠ” B의 λΆ€λΆ„ 집합이닀λ₯Ό λ– μ˜¬λ¦΄ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 κ°œλ…μ„ μ΅νžˆλ‹ˆ μ œλ„€λ¦­μ˜ extends도 이해가 λμŠ΅λ‹ˆλ‹€.

interface Person { name: string; } function sort<P extends Person>(people: P[]): P[] { return people.sort((a, b) => a.name > b.name ? 1 : -1); }

sort ν•¨μˆ˜μ˜ μΈμžλŠ” Person νƒ€μž…μ— ν• λ‹Ή κ°€λŠ₯ν•œ νƒ€μž…μ˜ 배열이어야 ν•©λ‹ˆλ‹€. κ·Έλž˜μ„œ μ œλ„€λ¦­ μ„ μ–Έμ—μ„œμ˜ extendsλŠ” λ²”μœ„λ₯Ό ν•œμ •ν•˜λŠ” μΌμ’…μ˜ 쑰건문으둜 λ™μž‘ν•˜κ²Œ λ©λ‹ˆλ‹€.

μž‰μ—¬ 속성 체크 (Excess Property Check)

ꡬ쑰적 타이핑 κ΄€μ μ—μ„œ μ˜ˆμ™Έ μΌ€μ΄μŠ€κ°€ μžˆμŠ΅λ‹ˆλ‹€.

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

β€” μ•„μ΄ν…œ 11. μž‰μ—¬ 속성 체크의 ν•œκ³„ μΈμ§€ν•˜κΈ°
let person: Person; person = { name: 'a', job: 'b', // ~~~~~~~ Type '{ name: string; job: string; }' is not assignable to type 'Person'. // Object literal may only specify known properties, and 'job' does not exist in type 'Person'. };

μ§€κΈˆκΉŒμ§€ 봀던 μ˜ˆμ œμ™€ 달리 객체 λ¦¬ν„°λŸ΄μ„ μ¦‰μ‹œ ν• λ‹Ήν•˜λ‹ˆ 속성을 μ—„κ²©ν•˜κ²Œ μ²΄ν¬ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. μž‰μ—¬ 속성 체크λ₯Ό μœ μš©ν•˜κ²Œ μ“Έ 수 μžˆλŠ” μΌ€μ΄μŠ€κ°€ λͺ‡ 가지 μžˆμŠ΅λ‹ˆλ‹€.

선택적(optional) μ†μ„±μ˜ μ˜€νƒ€ 검증

type Person = { name: string; job?: string; } let person: Person; const personWithTypo = { name: 'sh', jop: 'abc', }; person = personWithTypo; // OK? person = { name: 'sh', jop: 'abc', // ~~ Object literal may only specify known properties, // but 'jop' does not exist in type 'Person'. Did you mean to write 'job'? };

λ³€μˆ˜λ₯Ό μ„ μ–Έν•œ λ’€ ν• λ‹Ήν•  λ•ŒλŠ” μž‘μ§€ λͺ»ν–ˆλ˜ jopμ΄λΌλŠ” 선택적 μ†μ„±μ˜ μ˜€νƒ€λ₯Ό μž‘μ•„λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 가독성을 μœ„ν•΄ λ³€μˆ˜λ‘œ μΆ”μΆœν•΄ 미리 ν‘œν˜„ν•΄λ‘λŠ” κ²½μš°κ°€ λ§Žμ€λ° 자주 λ³€ν•˜λŠ” 객체인 경우 μ‹€μˆ˜λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•œ μˆ˜λ‹¨μœΌλ‘œ ν™œμš©ν•΄λ΄λ„ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

λ§€ν•‘λœ νƒ€μž…μ„ μ‚¬μš©ν•΄ 값을 λ™κΈ°ν™”ν•˜κΈ°

μΈν„°νŽ˜μ΄μŠ€μ— μƒˆλ‘œμš΄ 속성을 μΆ”κ°€ν•  λ•Œ, 선택을 κ°•μ œν•˜λ„λ‘ λ§€ν•‘λœ νƒ€μž…μ„ κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€.

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

React μ»΄ν¬λ„ŒνŠΈ μ΅œμ ν™” 기법 쀑 props의 λ³€ν™”λ₯Ό μ—„κ²©ν•˜κ²Œ 체크해 ν•„μš”ν•œ κ²½μš°μ—λ§Œ λ Œλ”λ§ν•˜λŠ” 기법이 μžˆμŠ΅λ‹ˆλ‹€. propsλ₯Ό 받지 μ•Šκ±°λ‚˜ μ›μ‹œ νƒ€μž…μœΌλ‘œ κ΅¬μ„±λœ 경우 React.memoλ₯Ό κ°μ‹ΈλŠ” 것 만으둜 λŒ€μ‘μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ 속성과 값이 자주 λ³€ν•˜λŠ” 경우라면 두 번째 인자인 μ‚¬μš©μž μ •μ˜ 비ꡐ ν•¨μˆ˜(custom comparison function)둜 μ œμ–΄ν•΄μ•Ό ν•©λ‹ˆλ‹€.

이 λ•Œ μž‰μ—¬ 속성 체크 κΈ°λ²•μœΌλ‘œ 비ꡐ ν•¨μˆ˜μ˜ μΈμžμ™€ props 속성을 동기화 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type Props = { x: number; y: number; radius: number; onClick: VoidFunction; }; function Point(props: Props) { return (...); } const REQUIRES_UPDATE: { [k in keyof Props]: boolean } = { x: true, y: true, radius: true, onClick: false, }; export default React.memo(Point, (prevProps, nextProps) => { let k: keyof Props; for (k in prevProps) { if (REQUIRES_UPDATE[k] && prevProps[k] !== nextProps[k]) { // μ—…λ°μ΄νŠΈ λŒ€μƒ 속성이고 값이 달라진 경우 λ‹€μ‹œ λ Œλ”λ§ν•˜κΈ° return false; } } return true; });

REQUIRES_UPDATE κ°μ²΄λŠ” 값이 λ³€ν•  λ•Œ μ—…λ°μ΄νŠΈν•΄μ•Ό ν•˜λŠ” ν•„λ“œλ₯Ό boolean κ°’μœΌλ‘œ κ΄€λ¦¬ν•©λ‹ˆλ‹€. νƒ€μž… μ •μ˜μ™€ λ™μ‹œμ— 객체 λ¦¬ν„°λŸ΄λ‘œ ν• λ‹Ήν•˜κΈ° λ•Œλ¬Έμ— μž‰μ—¬ 속성 체크 λŒ€μƒμ΄ λ©λ‹ˆλ‹€.

λ§Œμ•½ 포인트λ₯Ό 그릴 λ•Œ 색상이 ν•„μš”ν•΄μ‘Œλ‹€κ³  가정해보면, μ•„λž˜μ™€ 같이 props νƒ€μž…μ„ λ³€κ²½ν•˜κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€.

type Props = { x: number; y: number; radius: number; color: string; // μƒˆλ‘œμš΄ 속성 μΆ”κ°€ onClick: VoidFunction; }; const REQUIRES_UPDATE: { [k in keyof Props]: boolean } = { // ~~~~~~~~~~~~~~~ Property 'color' is missing in type '{ x: true; y: true; radius: true; onClick: false; }' // but required in type '{ x: boolean; y: boolean; radius: boolean; color: boolean; onClick: boolean; }'. x: true, y: true, radius: true, onClick: false, // `useCallback`이 적용된 ν•¨μˆ˜λΌλ©΄ 비ꡐ κ°€λŠ₯ };

이 λ•Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” REQUIRES_UPDATE 객체에 μƒˆλ‘œμš΄ μ†μ„±μ˜ μ—…λ°μ΄νŠΈ λŒ€μƒ μ—¬λΆ€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜λ‹€κ³  μ•Œλ €μ€λ‹ˆλ‹€. λ§Œμ•½ 이 과정이 μ—†μ—ˆλ‹€λ©΄ color 값이 λ°”λ€Œμ–΄λ„ UIκ°€ μ—…λ°μ΄νŠΈλ˜μ§€ μ•ŠλŠ” 버그가 생겼을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μž‰μ—¬ 속성 체크λ₯Ό 톡해 버그λ₯Ό 미연에 방지할 수 μžˆλŠ” 쒋은 μ‚¬λ‘€μž…λ‹ˆλ‹€.

μ‹€λ¬΄μ—μ„œ λ³΅μž‘ν•œ μ»΄ν¬λ„ŒνŠΈμ˜ λ Œλ”λ§ 횟수λ₯Ό 쀄이기 μœ„ν•΄ μ›μ΄ˆμ μΈ λ°©λ²•μœΌλ‘œ 속성 ν•˜λ‚˜ν•˜λ‚˜ μ²΄ν¬ν•˜κ±°λ‚˜ lodash.isEqual ν•¨μˆ˜λ‘œ κΉŠμ€ 비ꡐ(deep equal)λ₯Ό ν–ˆλ˜ 적이 μžˆμ—ˆλŠ”λ° μœ„ μ˜ˆμ œμ™€ 같이 μ •μ˜ν•΄λ‘λ©΄ μš”κ΅¬μ‚¬ν•­μ΄ λ³€ν•  λ•Œ νƒ€μž… 체크둜 버그λ₯Ό λ°©μ§€ν•˜κ³ , 비ꡐ μ—°μ‚° λΉ„μš©μ„ 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

그리고 μ•„λž˜μ™€ 같이 μ œλ„ˆλ¦­ 기반의 μœ ν‹Έ ν•¨μˆ˜λ‘œ μž‘μ„±ν•΄ μž¬μ‚¬μš© ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type Requirements<P extends object> = { [k in keyof Required<P>]: boolean; }; const createMemoComparator = <P extends object>( requirements: Requirements<P>, customizer?: (prevProps: P, nextProps: P) => boolean ) => { return (prevProps: P, nextProps: P) => { let k: keyof P; for (k in prevProps) { if (requirements[k] && prevProps[k] !== nextProps[k]) { return false; } } if (typeof customizer === 'function') { return customizer(prevProps, nextProps); } return true; }; }; type Props = { x: number; y: number; radius: number; onClick: VoidFunction; }; function Point(props: Props) { return (...); } export default React.memo(Point, createMemoComparator<Props>({ x: true, y: true, radius: true, onClick: false, });

마무리

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ ꡬ쑰적 타이핑에 κ΄€ν•΄ μ •λ¦¬ν•˜λ©° κ°€μž₯ μ–΄λ €μ› λ˜ 뢀뢄은 νƒ€μž… 연산을 μœ„ν•΄ νƒ€μž…μ€ κ°’μ˜ μ§‘ν•©μ΄λΌλŠ” κ°œλ…μ„ λŒ€μž…μ‹œν‚€λŠ” κ³Όμ •μ΄μ—ˆμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ νŒ€μ— κ³΅μœ ν–ˆμ„ λ•Œ 이 λΆ€λΆ„μ˜ μ„€λͺ…이 ν˜Όλ™λœλ‹€λŠ” ν”Όλ“œλ°±μ΄ λ§Žμ•˜μŠ΅λ‹ˆλ‹€.

A data type provides a set of values from which an expression (i.e. variable, function, etc.) may take its values

β€” Data type, Wikipedia

ν”„λ‘œκ·Έλž˜λ° 언어학에 μ •μ˜λœ λ‚΄μš©μž„μ—λ„ 동적 νƒ€μž… 언어인 μžλ°”μŠ€ν¬λ¦½νŠΈμ— μ΅μˆ™ν•΄μ Έ μžˆλ‹€ λ³΄λ‹ˆ μžμ—°μŠ€λŸ½κ²Œ 받아듀이지 λͺ»ν•˜κ²Œ 된 것 κ°™μŠ΅λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ 핡심 원리 쀑 ν•˜λ‚˜μΈ ꡬ쑰적 타이핑을 λ‹€μ‹œ ν•œλ²ˆ 정리해보면 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό λͺ¨λΈλ§ν•˜κΈ° μœ„ν•΄ κ°’μ˜ ν˜•νƒœλ₯Ό κΈ°μ€€μœΌλ‘œ ν• λ‹Ή μ—¬λΆ€λ₯Ό κ²°μ • μ§“λŠ” νŠΉμ„±μ„ 가짐
  • νƒ€μž…μ— μ •μ˜λ˜μ§€ μ•Šμ€ 속성과 같이 포함될 수 μžˆλ‹€λŠ” 점을 μœ μ˜ν•΄μ•Ό 함
  • 값을 κΈ°μ€€μœΌλ‘œ νŒλ‹¨ν•˜κΈ° λ•Œλ¬Έμ— ν…ŒμŠ€νŠΈ μž‘μ„± μ‹œ mock 객체λ₯Ό ν™œμš©ν•˜κΈ° 쉬움
  • μž‰μ—¬ 속성 μ²΄ν¬λŠ” ꡬ쑰적 νƒ€μ΄ν•‘μ˜ μ˜ˆμ™Έ μΌ€μ΄μŠ€λ‘œ 객체 λ¦¬ν„°λŸ΄μ„ ν• λ‹Ήν•  λ•Œ λΆˆν•„μš”ν•œ μ†μ„±μ΄λ‚˜ μ˜€νƒ€λ₯Ό 확인함

Footnotes

  1. 덕 νƒ€μ΄ν•‘μ΄λž€, 객체가 μ–΄λ–€ νƒ€μž…μ— λΆ€ν•©ν•˜λŠ” λ³€μˆ˜μ™€ λ©”μ„œλ“œλ₯Ό κ°€μ§ˆ 경우 객체λ₯Ό ν•΄λ‹Ή νƒ€μž…μ— μ†ν•˜λŠ” κ²ƒμœΌλ‘œ κ°„μ£Όν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€. "λ§Œμ•½ μ–΄λ–€ μƒˆκ°€ 였리처럼 κ±·κ³ , ν—€μ—„μΉ˜κ³ , κ½₯κ½₯κ±°λ¦¬λŠ” μ†Œλ¦¬λ₯Ό λ‚Έλ‹€λ©΄ λ‚˜λŠ” κ·Έ μƒˆλ₯Ό 였리라고 λΆ€λ₯Ό 것이닀." ↩

  2. λ“œ λͺ¨λ₯΄κ°„μ˜ 법칙 - μœ„ν‚€λ°±κ³Ό, 우리 λͺ¨λ‘μ˜ 백과사전 ↩