πŸ‘¨β€πŸ’»

Typescript μ΄ν•΄ν•˜κΈ° 3편

2021-02-22   |   개발

post-img

κΉƒν—™ TIL λ ˆν¬μ— κΈ°λ‘ν–ˆλ˜ λ‚΄μš©μ„ μ •λ¦¬ν•˜μ—¬ λΈ”λ‘œκΉ…ν•©λ‹ˆλ‹€.

λ™μ μœΌλ‘œ νƒ€μž…μ„ μΆ”λ‘ ν•˜λŠ” μ œλ„€λ¦­

μ œλ„€λ¦­ 문법은 같은 κ·œμΉ™μ„ μ—¬λŸ¬ νƒ€μž…μ— λ™μΌν•˜κ²Œ μ μš©ν•  수 μžˆλ„λ‘ 도와쀀닀. νƒ€μž… 정보가 λ™μ μœΌλ‘œ κ²°μ •λ˜κ²Œ ν•˜λŠ” μš”μ†Œμ΄κΈ° λ•Œλ¬Έμ΄λ‹€. κ³ μ •λœ νƒ€μž…μ„ λ§žμΆ°μ„œ μ‚¬μš©ν•˜λŠ” 것보닀 μœ μ—°ν•˜λ‹€. μ œλ„€λ¦­ 문법을 μ μš©ν•˜μ§€ μ•Šκ³ , ν•¨μˆ˜μ— μž…λ ₯κ°’κ³Ό λ°˜ν™˜κ°’μ˜ νƒ€μž…μ„ μΆ”μ •ν•˜λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ μ œλ„€λ¦­μ΄ μ–Όλ§ˆλ‚˜ νŽΈν•œμ§€ μ•Œ 수 μžˆλ‹€. (개인적으둜 μ œλ„€λ¦­μ΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ 제일 μ„Ήμ‹œν•œ 문법 κ°™λ‹€.)

function makeArray(defaultValue: string, size: number): string[]; function makeArray(defaultValue: number, size: number): number[]; function makeArray( defaultValue: number | string, size: number ): Array<string | number> { const arr: Array<string | number> = []; for (let i = 0; i < size; i += 1) { arr.push(defaultValue); } return arr; } makeArray(1, 5); // Array<number> makeArray('hi', 5); // Array<stirng>

μœ„μ˜ μ½”λ“œμ—μ„œ makeArray ν•¨μˆ˜λŠ” μž…λ ₯ν•˜λŠ” defaultValue의 νƒ€μž… ν˜•νƒœμ— λ”°λΌμ„œ λ°˜ν™˜κ°’μ„ μ•Œμ•„μ„œ μΆ”μ •ν•΄μ„œ λ°˜ν™˜ν•œλ‹€. ν•˜μ§€λ§Œ, defaultValue에 ν•„μš”ν•œ νƒ€μž…μ΄ λŠ˜μ–΄λ‚˜κ²Œ λœλ‹€λ©΄ νƒ€μž…μ— λŒ€ν•œ μ •μ˜λ₯Ό μ•„λž˜μ²˜λŸΌ ν•˜λ‚˜ν•˜λ‚˜ ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.

function makeArray(defaultVaule: boolean, size: number): boolean[]; function makeArray(defaultValue: number | string | boolean ...): Array <string | number | boolean> {};

λ°˜λ³΅μ€ λΆˆνŽΈν•΄μ•Ό ν•œλ‹€.

μ œλ„€λ¦­ 문법을 ν™œμš©ν•˜λ©΄ μœ„μ˜ makeArray ν•¨μˆ˜μ˜ μž…λ ₯값에 λŒ€ν•œ 리턴값 νƒ€μž… 좔정을 μ‰½κ²Œ ν•  수 μžˆλ‹€. μ‰½κ²Œ ν•  수 μžˆλ‹€λŠ” 말은 ν•¨μˆ˜μ˜ νƒ€μž…μ„ 일일이 지정할 ν•„μš”κ°€ μ—†λ‹€λŠ” 것이닀. μ œλ„€λ¦­ 문법은 < > 기호 μ•ˆμ— νƒ€μž… 정보λ₯Ό μž…λ ₯ν•˜λŠ” μ‹μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ‹€.

function makeArray2<T>(defaultValue: T, size: number): T[] { const arr: T[] = []; for (let i = 0; i < size; i += 1) { arr.push(defaultValue); } return arr; } const arr1 = makeArray2<string>('hi', 5); const arr2 = makeArray2('hi', 5); // νƒ€μž…μ„ μ•Œμ•„μ„œ μΆ”μ •ν•΄μ€€λ‹€.

μœ„μ—μ„œ 계속 반볡적으둜 λ™μΌν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό ν–ˆλ˜ λΆˆνŽΈμ„ μ œλ„€λ¦­ λ¬Έλ²•μœΌλ‘œ μ‰½κ²Œ μΆ”μ •μ‹œν‚¬ 수 μžˆλ‹€.


μ œλ„€λ¦­μœΌλ‘œ 자료ꡬ쑰 Stack κ΅¬ν˜„ν•΄λ³΄κΈ°

μ œλ„€λ¦­μœΌλ‘œ 자료ꡬ쑰λ₯Ό κ΅¬ν˜„ν•˜λ©΄ νŽΈν•˜λ‹€. μ΅œκ·Όμ— λ“€μ–΄μ˜¨ 데이터λ₯Ό κ°€μž₯ λ¨Όμ € λ‚΄λ³΄λ‚΄μ£ΌλŠ” Stack 자료ꡬ쑰λ₯Ό μ œλ„€λ¦­μœΌλ‘œ κ°€λ³κ²Œ κ΅¬ν˜„ν•΄λ³Έλ‹€.

class Stack<D> { private items: D[] = []; push(item: D) { this.items.push(item); } pop() { return this.items.pop(); } } const numStack = new Stack<number>(); numStack.push(10); let strStack: Stack<string>; strStack = numStack; // Error

μœ μ—°ν•¨μ—λ„ μ œμ•½μ΄ ν•„μš”ν• λ•Œκ°€ μžˆλ‹€.

νŠΉμ • λΌμ΄λΈŒλŸ¬λ¦¬λ‚˜ ν”„λ ˆμž„μ›Œν¬λŠ” ν•¨μˆ˜μ— μž…λ ₯받을 수 μžˆλŠ” νƒ€μž…μ˜ μ œν•œμ„ λ‘λŠ” κ²½μš°κ°€ μžˆλ‹€. 예λ₯Όλ“€μ–΄, λ¦¬μ•‘νŠΈμ˜ 속성값(props)λŠ” 객체 νƒ€μž…λ§Œ ν—ˆμš©μ΄ λœλ‹€. κ·Έλž˜μ„œ μ»΄ν¬λ„ŒνŠΈ ν•¨μˆ˜μ— μ œλ„€λ¦­ 문법을 μ‚¬μš©ν•  경우 μš°λ¦¬λŠ” νƒ€μž…μ„ μ œν•œμ‹œν‚¬ 수 μžˆμ–΄μ•Ό ν•œλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λ˜‘λ˜‘ν•΄μ„œ μ œλ„€λ¦­μ—λ„ νƒ€μž…μ„ μ œν•œμ‹œν‚¬ 수 μžˆλŠ” μž₯치λ₯Ό κ΅¬λΉ„ν•˜κ³  μžˆλ‹€. extends ν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•΄μ„œ μ œλ„€λ¦­μ΄ μΆ”μ •ν•  수 μžˆλŠ” νƒ€μž…μ˜ μ’…λ₯˜λ₯Ό μ œν•œν•  수 μžˆλ‹€. <T extends number | string> μ΄λ ‡κ²Œ μ œλ„€λ¦­μ„ μ •μ˜ν•˜λ©΄ <T>κ°€ μΆ”μ •ν•  수 μžˆλŠ” νƒ€μž…μ€ number, string으둜 κ΅­ν•œλœλ‹€. Tκ°€ number λ˜λŠ” string에 ν• λ‹Ή κ°€λŠ₯ν•΄μ•Ό ν•œλ‹€κ³  μƒκ°ν•˜λ©΄ λœλ‹€.

interface Animal { kindOf: string; age: number; } interface Shark extends Animal { habitat: string; } // keyof νƒ€μž… ν˜•νƒœλ‘œ μž‘μ„±μ΄ 되면, νƒ€μž…μ— μžˆλŠ” 속성 이름에 ν• λ‹Ή κ°€λŠ₯ν•œ νƒ€μž…μ΄λΌλŠ” 뜻. // μ—¬κΈ°μ„œλŠ” Animal의 kindOf λ˜λŠ” age둜 νƒ€μž…μ΄ μ •ν•΄μ Έμ•Ό ν•œλ‹€. function swapProperty<T extends Animal, K extends keyof Animal>( p1: T, p2: T, key: K ): void { const temp = p1[key]; p1[key] = p2[key]; p2[key] = temp; } const p1: Shark = { kindOf: 'fish', age: 100, habitat: 'deepSea' }; const p2: Shark = { kindOf: 'whale', age: 190, habitat: 'shallowSea' }; swapProperty(p1, p2, 'age'); swapProperty(p1, p2, 'habitat'); // Error : Animalμ—λŠ” habitatμ΄λΌλŠ” νƒ€μž… ν‚€κ°€ μ—†κΈ° λ•Œλ¬Έ!



νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ§΅λ“œ νƒ€μž…

λ§΅λ“œ νƒ€μž…μ˜ κΈ°λ³Έ 문법

λ§΅λ“œ νƒ€μž…μ€ 기본적으둜 객체 ν˜•νƒœμ˜ νƒ€μž…μ΄λ‹€. κ·Έλž˜μ„œ μΈν„°νŽ˜μ΄μŠ€μ™€ 같이 객체 ν˜•νƒœμ˜ νƒ€μž…μ— μ–΄λ–€ 처리λ₯Ό ν•΄μ£ΌκΈ° μœ„ν•΄μ„œ μ‚¬μš©λœλ‹€. 보톡, μΈν„°νŽ˜μ΄μŠ€μ˜ 속성듀을 readonly, optional μ†μ„±μœΌλ‘œ λ³€κ²½ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ‚¬μš©λœλ‹€.

μ‚¬μš©ν•˜λŠ” 방법은 μ€‘κ΄„ν˜Έ({})μ•ˆμ— λŒ€κ΄„ν˜Έ([])둜 속성을 ν‘œκΈ°ν•˜λŠ” 방식을 μ‚¬μš©ν•œλ‹€. μ œλ„€λ¦­ 처럼 νƒ€μž…μ˜ 이름을 κ°œλ°œμžκ°€ μ§€μ •ν•˜κ³  in이라고 ν•˜λŠ” ν‚€μ›Œλ“œλ‘œ μ†μ„±μ˜ 이름을 λ’€μ—μ˜€λŠ” κ²ƒλ“€λ‘œ μΉ˜ν™˜ν•œλ‹€κ³  μƒκ°ν•˜λ©΄ νŽΈν•˜λ‹€. 그러면 λŒ€κ΄„ν˜Έ λ°–μ—μ„œ μ •μ˜λœ νƒ€μž…μ΄ κ·Έ μ†μ„±λ“€μ˜ νƒ€μž…μ΄ λœλ‹€.

// λ‘κ°œμ˜ T1은 같은 ν‘œκΈ°μ΄λ‹€. type T1 = { [K in 'prop1' | 'prop2']: boolean }; type T1 = { prop1: boolean; prop2: boolean; };

마치 ν•¨μˆ˜μ²˜λŸΌ μ‚¬μš©λ˜λŠ” νƒ€μž…μ΄λΌ 'μœ ν‹Έλ¦¬ν‹° νƒ€μž…'이라고 λΆˆλ¦°λ‹€.

μΈν„°νŽ˜μ΄μŠ€μ˜ λͺ¨λ“  μ†μ„±μ˜ νƒ€μž…μ„ μ „ν˜€ λ‹€λ₯Έ νƒ€μž…μœΌλ‘œ ν•œ λ²ˆμ— λ°”κΏ€λ•Œλ„ λ§΅λ“œ νƒ€μž…μ΄ μ‚¬μš©λœλ‹€. μ•„λž˜μ˜ μ½”λ“œ μ˜ˆμ‹œλ₯Ό ν•œ 번 보자.

interface Shark { name: string; age: number; } type MakeType<T> = { [K in keyof T]?: boolean }; const sharkMap: MakeType<Shark> = {}; sharkMap.name = true; sharkMap.age = 123; // Error: number νƒ€μž…μ€ 뢈린 νƒ€μž…μ— 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

MakeTypeμ΄λΌλŠ” νƒ€μž…μ€ μ œλ„€λ¦­μœΌλ‘œ νŠΉμ • μΈν„°νŽ˜μ΄μŠ€ λ˜λŠ” 객체λ₯Ό λ°›μ•„μ„œ κ·Έ μΈν„°νŽ˜μ΄μŠ€μ˜ 속성을 μ˜΅μ…”λ„ν•˜κ²Œ λ§Œλ“€κ³  뢈린 νƒ€μž…μ„ ν• λ‹Ήν•˜κ²Œ ν•œλ‹€. 우리의 μΉœμ ˆν•œ VSCodeλŠ” 이 νƒ€μž…μ˜ λ³€ν™˜μ„ 잘 μΊμΉ˜ν•œλ‹€.

mapped


μ†μ„±μ˜ νƒ€μž…μ€ μœ μ§€ν•˜λ©΄μ„œ λ³€ν™”μ£ΌκΈ°

μœ„μ— μ˜ˆμ‹œμ²˜λŸΌ μΈν„°νŽ˜μ΄μŠ€λ‚˜ 객체의 속성듀에 λŒ€ν•œ νƒ€μž…μ„ μΌκ΄„μ μœΌλ‘œ λ³€κ²½ν•  λ•Œλ§Œ λ§΅λ“œ νƒ€μž…μ„ μ‚¬μš©ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€. 였히렀 μ†μ„±λ“€μ˜ νƒ€μž…μ„ μœ μ§€ν•˜λ©΄μ„œ readonly 값을 μ£Όκ±°λ‚˜ optionalν•œ μ†μ„±μœΌλ‘œ λ§Œλ“€ ν•„μš”λ„ μžˆλ‹€. 그런 처리 μ—­μ‹œ λ§΅λ“œ νƒ€μž…μœΌλ‘œ ν•˜λ©΄ νŽΈν•˜λ‹€.

type Readonly<T> = { readonly [P in keyof T]: T[P] }; type Partial<T> = { [P in keyof T]?: T[P] }; type T1 = Readonly<Shark>; type T2 = Partial<Shark>;

μœ„μ˜ μ½”λ“œμ—μ„œ T[P]λΌλŠ” 문법이 μ œλ„€λ¦­μœΌλ‘œ λ‘˜λŸ¬μ‹Έμ—¬μ„œ 쑰금 μ΄ν•΄ν•˜κΈ° νž˜λ“€ 수 μžˆλ‹€. κ°„λ‹¨ν•˜κ²Œ λŒ€μž…μ˜ 방식을 λ– μ˜¬λ €λ³΄λ©΄ μ’‹λ‹€. Tμ—λŠ” κ°μ²΄λ‚˜ μΈν„°νŽ˜μ΄μŠ€κ°€ λŒ€μž…λ  것이닀. PλŠ” keyof ν‚€μ›Œλ“œλ‘œ T에 λŒ€μž…λœ μΈν„°νŽ˜μ΄μŠ€λ‚˜ 객체의 속성 이름듀이 각각 λŒ€μž…λœλ‹€. κ·Έλž˜μ„œ T[P]라고 된 뢀뢄을 λœ―μ–΄λ³΄λ©΄ 'μΈν„°νŽ˜μ΄μŠ€[key]'의 νƒ€μž…μ„ 띄어라 λΌλŠ” λœ»μ΄λ‹€.

κ·Έλž˜μ„œ μœ„μ˜ T1은 Shark μΈν„°νŽ˜μ΄μŠ€μ˜ μ†μ„±λ“€μ˜ νƒ€μž…μ€ μœ μ§€ν•˜λ©΄μ„œ, 각 속성에 readonly 속성을 λΆ€μ—¬ν–ˆλ‹€. T2λŠ” μ˜΅μ…”λ„ μ†μ„±μœΌλ‘œ λ³€κ²½ν–ˆμ„ 뿐이닀.


νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ κΈ°λ³Έ λ‚΄μž₯ λ§΅λ“œνƒ€μž…

μœ„μ—μ„œ κ°„λ‹¨ν•˜κ²Œ ꡬ좕해본 Readonly<T>와 Partial<T>λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 기본적으둜 가지고 μžˆλŠ” λ‚΄μž₯ν˜• λ§΅λ“œνƒ€μž…μ΄λ‹€. κ·Έλž˜μ„œ μœ„μ—μ„œ 처럼 λ”°λ‘œ νƒ€μž…μ„ μ •μ˜ν•˜μ§€ μ•Šκ³ , λ°”λ‘œ μ‚¬μš©ν•  수 μžˆλ‹€.

또 λ‹€λ₯Έ λ‚΄μž₯ λ§΅λ“œ νƒ€μž…μ—λŠ” Pick이 μžˆλ‹€. 이것은 μΈν„°νŽ˜μ΄μŠ€λ‚˜ 객체에 λŒ€ν•΄μ„œ νŒŒμƒ μƒν’ˆμ„ λ§Œλ“œλŠ” λŠλ‚Œμ˜ λ§΅λ“œ νƒ€μž…μ΄λ‹€. μ•„λž˜μ˜ μ˜ˆμ‹œ μ½”λ“œλ‘œ νŒŒμ•…ν•΄λ³΄μž.

type Pick<T, K extends keyof T> = { [P in K]: T[P] }; type T3 = Pick<Shark, 'age'>; type T4 = Pick<Shark, 'age' | 'name'>;

μ œλ„€λ¦­μ˜ 첫 μš”μ†ŒλŠ” νŒŒμƒμƒν’ˆμ„ λ§Œλ“€ μΈν„°νŽ˜μ΄μŠ€(T)κ°€ μ˜¨λ‹€. λ‹€μŒ μš”μ†Œλ‘œλŠ” κ·Έ μΈν„°νŽ˜μ΄μŠ€μ˜ 킀듀을 μ˜λ―Έν•˜λŠ” KλΌλŠ” μš”μ†Œκ°€ T의 ν™•μž₯성을 μœ μ§€ν•œμ±„λ‘œ μ˜¨λ‹€. λ§΅λ“œ νƒ€μž…μ˜ μ •μ˜λΆ€λΆ„μ„ 보면 PλŠ” K의 μœ λ‹ˆμ˜¨λ“€λ‘œ κ΅¬μ„±λ˜κ³  μΈν„°νŽ˜μ΄μŠ€μ˜ νƒ€μž…μ€ μœ μ§€λ˜λŠ” 것을 μ•Œ 수 μžˆλ‹€.

κ·Έλž˜μ„œ T3라고 ν•˜λŠ” νƒ€μž…μ€ Shark μΈν„°νŽ˜μ΄μŠ€μ˜ λͺ¨λ“  속성인 name, ageλ₯Ό κ°€μ§ˆ μˆ˜λ„ 있고, μœ„μ—μ„œμ²˜λŸΌ age만 κ°€μ§ˆ μˆ˜λ„ μžˆλ‹€.


또 λ‹€λ₯Έ λ‚΄μž₯ λ§΅λ“œ νƒ€μž…μ—λŠ” Recordκ°€ μžˆλ‹€. μ—­μ‹œ μ½”λ“œλ‘œ νŒŒμ•…ν•΄λ³΄μž.

type Record<K extends number, T> = { [P in K]: T }; type T4 = Recorded<3 | 5, Shark>;

Record λ§΅λ“œ νƒ€μž…λ„ μ—­μ‹œ μ œλ„€λ¦­μœΌλ‘œ κ·Έ μ‚¬μš©μ„±μ„ λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€. 첫 μš”μ†Œμ—λŠ” μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€μ— λ°˜μ˜ν•  속성값을 μœ λ‹ˆμ˜¨ ν˜•νƒœλ‘œ λ„£μ–΄μ„œ, 이게 μ–΄λ–€ νƒ€μž…μœΌλ‘œ ν™•μž₯된 것인지 μΈμ‹μ‹œμΌœμ•Ό ν•œλ‹€. μœ„μ˜ μ½”λ“œμ—μ„œλŠ” number νƒ€μž…μ— ν• λ‹Ή κ°€λŠ₯ν•œ K듀을 μž…λ ₯ν•΄μ•Ό ν•œλ‹€.

그리고 μ œλ„€λ¦­μ˜ λ‘λ²ˆμ§Έ μš”μ†Œμ— νƒ€μž…μœΌλ‘œ λ„£κ³  싢은 μΈν„°νŽ˜μ΄μŠ€ λ˜λŠ” νƒ€μž…μ„ λ„£μ–΄μ„œ 각 속성이 μ–΄λ–€ νƒ€μž…μ„ μ§€λ‹ˆκ²Œ ν•˜λŠ”μ§€ μ•Œλ €μ€€λ‹€. μœ„μ˜ μ½”λ“œμ—μ„œλŠ” Shark라고 ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ„ λ„£μ—ˆλ‹€. κ·Έλž˜μ„œ 결과적으둜

interface T4 { 3: Shark; 5: Shark; }

λΌλŠ” νƒ€μž…μ΄ μ •μ˜ 된 것과 λ§ˆμ°¬κ°€μ§€λ‹€.


λ§΅λ“œ νƒ€μž…μœΌλ‘œ Enum νƒ€μž… κ΅¬μ„±μ˜ μ‹€μˆ˜ 쀄이기

λ§΅λ“œ νƒ€μž…μœΌλ‘œ μ΄λ„˜ νƒ€μž…μ„ 쑰금 νŽΈν•˜κ²Œ(?) 관리할 수 μžˆλ‹€. λŒ€κ΄„ν˜Έ μ•ˆμ—μ„œ λ°˜λ³΅ν•˜λŠ” μš”μ†Œμ— μ΄λ„˜ νƒ€μž…μ„ λ„£μ–΄μ„œ 빠진 μš”μ†Œκ°€ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” λ°©μ‹μœΌλ‘œ λ§΅λ“œ νƒ€μž…μ„ μ‚¬μš©ν•œλ‹€. 직접 μ˜ˆμ‹œ μ½”λ“œλ₯Ό 보자.

enum Fruit { Apple, // 0 Orange, Banana, Kiwi } const Fruit_Price: { [K in Fruit]: number } = { [Fruit.Apple]: 1000, [Fruit.Orange]: 1500, 2: 1200 // Error: Kiwi에 λŒ€ν•œ κ°’ μ •μ˜κ°€ μ—†μŠ΅λ‹ˆλ‹€. };

Fruit라고 ν•˜λŠ” μ΄λ„˜ νƒ€μž…μ˜ μš”μ†Œλ“€μ„ K둜 λ°›κ³  μžˆλ‹€. κ·Έ λ•Œ 각각의 K에 λŒ€ν•œ νƒ€μž…μ€ numberκ°€ λœλ‹€. κ·Έλž˜μ„œ Fruit μ΄λ„˜μ— μžˆλŠ” λͺ¨λ“  μš”μ†Œμ— λŒ€ν•œ 숫자 값이 μ •μ˜λ˜μ–΄μ•Ό ν•œλ‹€. [Fruit.Apple] ν˜•μ‹μœΌλ‘œ 뢀여해도 되고, 2: 1200처럼 μˆ«μžν˜• μ΄λ„˜μ˜ νŠΉμ§•μ„ 잘 살렀도 λœλ‹€. 빠진 μš”μ†Œκ°€ μžˆλ‹€λ©΄ λ§΅λ“œ νƒ€μž…μ΄ μ•Œμ•„μ„œ μ—λŸ¬λ₯Ό 보내쀄 것이닀.



νƒ€μž…μŠ€ν¬λ¦½νŠΈ 쑰건뢀 νƒ€μž…

쑰건뢀 νƒ€μž… κΈ°λ³Έ 문법

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μ‚Όν•­μ—°μ‚°μžμ™€ μƒκΉ€μƒˆκ°€ λΉ„μŠ·ν•œ νƒ€μž… μ •μ˜κ°€ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—λ„ μžˆλ‹€. 쑰건뢀 νƒ€μž…μ΄ 그것이닀. κΈ°λ³Έ 문법은 T extends U ? X : Y의 ν˜•νƒœλ‹€. 정말 μ‚Όν•­μ—°μ‚°μžμ™€ 거의 λ™μΌν•œ λͺ¨μ–‘이닀. μ΄λ•Œ μ£Όμ˜ν•  점은 μ‚Όν•­μ—°μ‚°μžκ°€ 쑰건에 따라 λ‹€λ₯Έ 값을 λΆ€μ—¬ν•œλ‹€λ©΄, 쑰건뢀 νƒ€μž…μ€ λ‹€λ₯Έ νƒ€μž…μ„ λΆ€μ—¬ν•œλ‹€λŠ” 이야기닀. μ œλ„€λ¦­ Tκ°€ U νƒ€μž…μ— 할당이 κ°€λŠ₯ν•˜λ‹€λ©΄ (T의 νƒ€μž… λ²”μœ„κ°€ U보닀 μž‘λ‹€λ©΄) X νƒ€μž…μ„ 가지고, 그렇지 μ•ŠμœΌλ©΄ Y νƒ€μž…μ„ 가진닀.

쑰건뢀 νƒ€μž…μ—μ„œ μœ λ‹ˆμ˜¨ νƒ€μž…μ„ μ μš©ν•˜κ²Œ 되면 μš°λ¦¬κ°€ μ•„λŠ” μœ λ‹ˆμ˜¨ νƒ€μž… 성립과 쑰금 λ‹€λ₯Έ κ²°κ³Όκ°€ λ‚˜μ˜¨λ‹€. μ•„λž˜μ˜ μ½”λ“œλ₯Ό 보자.

type IsStringType<T> = T extends string ? 'string' : 'not string'; type T1 = IsStringType<string | number>; // string | not string type T2 = IsStringType<string> | IsStringType<number>; // = T1

νƒ€μž… T1의 경우 string | numberκ°€ string νƒ€μž…λ³΄λ‹€ λ²”μœ„κ°€ 크기 λ•Œλ¬Έμ— 기본적으둜 string νƒ€μž…μ— 할당이 λΆˆκ°€ν•˜λ‹€. κ·Έλž˜μ„œ 'not string' νƒ€μž…μ΄ λ˜μ–΄μ•Ό ν•œλ‹€κ³  생각이 λ“€μ§€λ§Œ, 쑰건뢀 νƒ€μž…μ—μ„œ μœ λ‹ˆμ˜¨ νƒ€μž…μ„ μ μš©ν•˜λ©΄ 두 νƒ€μž…μ„ λͺ¨λ‘ κ°€μ§ˆ 수 μžˆκ²Œλœλ‹€. κ·Έλž˜μ„œ 사싀상 T1의 νƒ€μž…μ€ T2의 νƒ€μž…μ„ μ •ν•˜λŠ” 것과 방식이 κ°™λ‹€.

μœ„μ˜ μ½”λ“œλ₯Ό μ‰½κ²Œ μ„€λͺ…ν•˜λ©΄, T에 λ“€μ–΄μ˜¨ μœ λ‹ˆμ˜¨ νƒ€μž…μ„ 각각 string에 ν• λ‹Ήν•  수 μžˆλŠ”μ§€λ₯Ό λΉ„κ΅ν•˜λŠ” 것이 쑰건뢀 νƒ€μž…μ΄λΌκ³  보면 λœλ‹€. string은 ν• λ‹Ή κ°€λŠ₯ν•˜μ§€λ§Œ, numberλŠ” string에 할당이 λΆˆκ°€ν•˜κΈ° λ•Œλ¬Έμ— T1이 string | not string 의 νƒ€μž…μ„ κ°€μ§€λŠ” 것이닀.


Exclude와 Extract νƒ€μž…

νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ‚΄μž₯ νƒ€μž…μ€‘μ— Exclude, Extract νƒ€μž…μ΄ μžˆλ‹€. 단어 뜻과 λΉ„μŠ·ν•˜κ²Œ λ™μž‘ν•œλ‹€. 이것을 쑰건뢀 νƒ€μž… + μœ λ‹ˆμ˜¨ μ œλ„€λ¦­μœΌλ‘œ μ‰½κ²Œ μž‘μ„±ν•  수 μžˆμ–΄μ„œ 기둝을 ν•΄λ³Έλ‹€. 이것을 κΈ°λ‘ν•˜κΈ°μ— μ•žμ„œμ„œ μœ λ‹ˆμ˜¨ νƒ€μž…μ— 'never' νƒ€μž…μ΄ μžˆλ‹€λ©΄ 그것은 μ œμ™Έλœλ‹€.

// U에 ν• λ‹Ή κ°€λŠ₯ν•˜μ§€ μ•Šμ€ T만 남겨라 type exclude<T, U> = T extends U ? never : T; type T5 = exclude<1 | 3 | 5 | 7, 1 | 5 | 9>; // 3 | 7 // U에 ν• λ‹Ή κ°€λŠ₯ν•œ T만 남겨라 type extract<T, U> = T extends U ? T : never; type T6 = extract<1 | 3 | 5 | 7, 1 | 5 | 9>; // 1 | 5
  • Exclude νƒ€μž…μ€ U에 ν• λ‹Ή κ°€λŠ₯ν•œ TλŠ” λΉΌκ³  λ‚˜λ¨Έμ§€λ₯Ό νƒ€μž…μœΌλ‘œ κ°€μ§€κ²Œ ν•΄μ£ΌλŠ” νƒ€μž…μ΄λ‹€. <1 | 3 | 5 | 7> μ—μ„œ 1, 5λŠ” 할당이 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— μ œμ™Έν•˜κ³  <3 | 7> 만 νƒ€μž…μœΌλ‘œ λ°˜μ˜λœλ‹€.
  • ExtractλŠ” U에 ν• λ‹Ή κ°€λŠ₯ν•œ T만 μΆ”μΆœν•˜λŠ” νƒ€μž…λ‹ˆλ‹€. μœ„μ˜ μ„€λͺ…κ³Ό λ°˜λŒ€λ‘œ λ™μž‘ν•œλ‹€.

ReturnType

ReturnType μ—­μ‹œ νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ‚΄μž₯ νƒ€μž…μ΄λ‹€. μ΄λ¦„μ—μ„œ μœ μΆ”ν•  수 μžˆλ“―μ΄ ν•¨μˆ˜ νƒ€μž…μ˜ 리턴값을 κ·Έ νƒ€μž…μœΌλ‘œ κ°€μ§€κ²Œ ν•΄μ£ΌλŠ” μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄λ‹€. λ‚΄λΆ€κ°€ 쑰건뢀 νƒ€μž…μœΌλ‘œ κ΅¬μ„±λ˜μ–΄ 있기 λ•Œλ¬Έμ— κ°„λ‹¨ν•˜κ²Œ μ•Œμ•„λ³Ό 수 μžˆλ‹€. ReturnType을 μ•Œμ•„λ³΄κΈ° μœ„ν•΄μ„œλŠ” inferλΌλŠ” ν‚€μ›Œλ“œλ₯Ό μ•Œμ•„μ•Ό ν•œλ‹€. infer ν‚€μ›Œλ“œ μ—­μ‹œ 사전적인 μ˜λ―Έμ™€ λΉ„μŠ·ν•œλ°, νƒ€μž… 좔둠을 μœ„ν•΄μ„œ μ‚¬μš©ν•˜λŠ” ν‚€μ›Œλ“œλ‘œ λ°˜ν™˜κ°’μ„ μΆ”λ‘ ν•˜λŠ”λ° μ‚¬μš©λœλ‹€. infer ν‚€μ›Œλ“œλŠ” 값이 정해지지 μ•Šμ€ νƒ€μž… μ•žμ— μ™€μ„œ κ·Έ κ°’μ˜ νƒ€μž…μ΄ 무엇인지 μΆ”λ‘ ν•΄μ€€λ‹€. (infer U)[]라고 λ˜μ–΄ 있으면 μΆ”λ‘ λ˜λŠ” U의 νƒ€μž…μ— λ”°λ₯Έ 배열이 λœλ‹€.

type returntype<T> = T extends (...args: any[]) => infer R ? R : any; type T7 = returntype<() => string>; // T7: string; function f1(s: string): number { return s.length; } type T8 = returntype<typeof f1>; // f1의 λ°˜ν™˜κ°’μ„ 가진닀. T8: number

μœ„μ˜ μ½”λ“œμ—μ„œ μ‚΄νŽ΄λ³΄λ©΄, μ œλ„€λ¦­ Tκ°€ Rμ΄λΌλŠ” νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ” κ²ƒμœΌλ‘œ μΆ”λ‘ λ˜λŠ” ν•¨μˆ˜μ— ν• λ‹Ή κ°€λŠ₯ν•œμ§€ μ‘°κ±΄λΆ€λ‘œ νŒŒμ•…ν•œλ‹€. 할당이 κ°€λŠ₯ν•˜λ©΄ κ·Έ νƒ€μž…μ€ R이 되고 그렇지 μ•ŠμœΌλ©΄ anyκ°€ λ°˜μ˜λœλ‹€. T8의 νƒ€μž…μ„ κ²°μ •ν•˜λŠ” λΆ€λΆ„μ—μ„œ ν•¨μˆ˜ν˜• νƒ€μž…μΈμ§€λ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄μ„œ typeof ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œ 것도 확인할 ν•„μš”κ°€ μžˆλ‹€.


Omit νƒ€μž…

Omitνƒ€μž… μ—­μ‹œ νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ‚΄μž₯ νƒ€μž… 쀑 ν•˜λ‚˜λ‹€. μ‚¬μš©μ€ Omit<T, U> ν˜•μ‹μœΌλ‘œ ν•˜κ³ , μ œλ„€λ¦­ T νƒ€μž…μ—μ„œ U의 ν‚€ 속성에 ν•΄λ‹Ήν•˜λŠ” 것을 μ œμ™Έν•œ λ‚˜λ¨Έμ§€λ₯Ό λ°˜ν™˜ν•œλ‹€. νƒ€μž… μ •μ˜κ°€ ν•¨μˆ˜κ°€ μ•„λ‹˜μ—λ„ λΆˆκ΅¬ν•˜κ³  ν•¨μˆ˜μ™€ 같이 무언가λ₯Ό λ°˜ν™˜ν•˜λŠ” λŠλ‚Œμ΄ κ°•ν•˜λ‹€.

type omit<T, U extends keyof T> = Pick<T, Exclude<keyof T, U>>; type T11 = omit<Shark, 'name' | 'age'>; // T11: { teeth: boolean }

μœ„μ˜ μ½”λ“œμ—μ„œ UλŠ” μ œλ„€λ¦­ T의 속성 이름듀 쀑에 ν• λ‹Ή κ°€λŠ₯ν•œ μš”μ†Œλ‘œ κ΅¬μ„±λœ μœ λ‹ˆμ˜¨μ΄λ‹€. 그것듀을 Tμ—μ„œ Exclude νƒ€μž…μœΌλ‘œ μ œμ™Έν•΄μ£Όκ³ , 남은 것을 Pick ν•˜λŠ” μˆœμ„œλ‘œ λ™μž‘ν•œλ‹€. μ‰½κ²Œ μƒκ°ν•΄μ„œ μΈν„°νŽ˜μ΄μŠ€μ—μ„œ νŠΉμ • 속성을 μ œμ™Έν•œ λ‚˜λ¨Έμ§€λ₯Ό νƒ€μž…μœΌλ‘œ κ°€μ§€κ²Œ ν•΄μ£ΌλŠ” μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄λ‹€. λ¦¬μ•‘νŠΈμ—μ„œ propsλ₯Ό λΆ€λΆ„μ μœΌλ‘œ λ°›λŠ” μ»΄ν¬λ„ŒνŠΈ μ½”λ“œμ— 적합할 것 κ°™λ‹€.


μ—¬λŸ¬ νƒ€μž…κ³Ό ν˜Όμš©ν•΄μ„œ μ‚¬μš©ν•˜κΈ°

쑰건뢀 νƒ€μž…μ€ μ—¬λŸ¬ λ‹€λ₯Έ νƒ€μž…κ³Ό ν˜Όμš©ν•΄μ„œ μƒˆλ‘œμš΄ μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ„ λ§Œλ“€ λ•Œ μœ μš©ν•˜κ²Œ μ‚¬μš©λœλ‹€. κ°€λ³κ²Œ ν•˜λ‚˜μ˜ μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ„ λ§Œλ“€λ©΄μ„œ 이 뢀뢄을 이해해본닀. μ•„λž˜μ˜ μ½”λ“œλ₯Ό μˆœμ„œμ μœΌλ‘œ λΆ„ν•΄ν•΄λ³΄λ©΄μ„œ μ΄ν•΄ν•˜μž.

type StringPropertyNames<T> = { [K in keyof T]: T[K] extends string ? K : never; }[keyof T]; interface Shark { name: string; teeth: boolean; age: string; } type T9 = StringPropertyNames<Shark>; // "name" | "age"

StringPropertyNames라고 ν•˜λŠ” νƒ€μž…μ΄ κ²°μ •λ˜λŠ” 방식이 쑰금 λ³΅μž‘ν•˜λ‹€. λ§΅λ“œ νƒ€μž…κ³Ό 쑰건뢀 νƒ€μž…, 그리고 [keyof T]λΌλŠ” ν‘œν˜„κΉŒμ§€.

  1. μš°μ„  λ§΅λ“œ νƒ€μž…μ—μ„œ μ œλ„€λ¦­ T νƒ€μž…μ˜ 속성 μ΄λ¦„λ“€λ‘œ Kκ°€ κ΅¬μ„±λœλ‹€. (μœ λ‹ˆμ˜¨ νƒ€μž…)
  2. κ·Έ λ•Œμ˜ T[K] 즉, K μ†μ„±μ˜ νƒ€μž…κ°’μ΄ string인지 μ•„λ‹Œμ§€λ₯Ό 쑰건뢀 νƒ€μž…μ—μ„œ κ²°μ •ν•œλ‹€. string이라면 Kλ₯Ό νƒ€μž…μœΌλ‘œ μ§€μ •ν•˜λ‹ˆκΉŒ T의 속성 이름 자체λ₯Ό νƒ€μž…μœΌλ‘œ λ°°μ •ν•œλ‹€λŠ” λœ»μ΄λ‹€. μ•„λ‹κ²½μš° never둜 μ œμ™Έμ‹œν‚¨λ‹€.
  3. [keyof T]둜 ν‘œμ‹œλœ 뢀뢄은 말 κ·ΈλŒ€λ‘œ μ œλ„€λ¦­ T의 속성 이름을 이 μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄ λ°˜ν™˜ν•˜λŠ” νƒ€μž…μœΌλ‘œ μ§€μ •ν•˜κ² λ‹€λŠ” λœ»μ΄λ‹€.

결과적으둜 Shark μΈν„°νŽ˜μ΄μŠ€μ— 이 μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ„ μ μš©ν•˜λ©΄ 속성 νƒ€μž…μ΄ string인 name, ageκ°€ κ±ΈλŸ¬μ§€κ³  (λ§΅λ“œ & 쑰건뢀 νƒ€μž…), κ·Έλ•Œμ˜ 속성 이름 μžμ²΄κ°€ μœ λ‹ˆμ˜¨ ν˜•μ‹μœΌλ‘œ([keyof T]) T9의 νƒ€μž…μ— λ°˜μ˜λœλ‹€.


λͺ©μ°¨

μ΄μ–΄μ§€λŠ” κΈ€,