๋ฐ˜์‘ํ˜•

ํƒ€์ž… ์ถ”๋ก 


๊ธฐ๋ณธ

๐Ÿ’ก ํƒ€์ž…์ถ”๋ก ์ด๋ž€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ•ด์„ํ•ด ๋‚˜๊ฐ€๋Š” ๋™์ž‘์„ ์˜๋ฏธํ•œ๋‹ค.

 

์•„๋ž˜์ฒ˜๋Ÿผ x ์— ๋Œ€ํ•œ ํƒ€์ž…์„ ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๋”๋ผ๋„ ๋ณ€์ˆ˜ x์— ์ˆซ์ž 3์„ ํ• ๋‹นํ–ˆ์œผ๋ฏ€๋กœ number ํƒ€์ž…์œผ๋กœ ๊ฐ„์ฃผํ•œ๋‹ค. ์ด์ฒ˜๋Ÿผ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ฑฐ๋‚˜ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ํƒ€์ž…์ด ์ถ”๋ก ๋œ๋‹ค. ๋ณ€์ˆ˜, ์†์„ฑ, ํ•จ์ˆ˜ ์ธ์ž์˜ ๊ธฐ๋ณธ ๊ฐ’, ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ๊ฐ’ ๋“ฑ์„ ์„ค์ •ํ•  ๋•Œ๋„ ํƒ€์ž… ์ถ”๋ก ์ด ์ผ์–ด๋‚œ๋‹ค.

let x = 3; // ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ํ• ๋‹นํ•จ์œผ๋กœ์„œ x๋Š” number ํƒ€์ž…์ด๋ผ๋Š” ์ถ”๋ก ์ด ์ผ์–ด๋‚ฌ๋‹ค.

 

 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ DetailedDropdown ์ธํ„ฐํŽ˜์ด์Šค๋Š” extends ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ–ˆ์œผ๋ฏ€๋กœ Dropdown ์ธํ„ฐํŽ˜์ด์Šค์˜ ์†์„ฑ๋„ ๊ฐ€์ง„๋‹ค. DetailedDropdown<number>์˜ <number> ํƒ€์ž…์€ DetailedDropdown์— ์žˆ๋Š” <T> ๋กœ ๋„˜์–ด๊ฐ„ ํ›„, ๋‹ค์‹œ Dropdown<T> ์ธํ„ฐํŽ˜์ด์Šค์˜ <T>๋กœ ๋„˜๊ฒจ์ ธ ์ตœ์ข…์ ์œผ๋กœ Dropdown์— ์žˆ๋Š” value: T๋Š” number ํƒ€์ž…์„ ๊ฐ–๋Š”๋‹ค. ์ด๋Ÿฐ ๊ณผ์ •์„ ํ†ตํ•ด ํƒ€์ž… ์ถ”๋ก ์ด ์ด๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์— value์— number ํƒ€์ž…์ด ์•„๋‹Œ ๋‹ค๋ฅธ ํƒ€์ž…์„ ํ• ๋‹นํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

interface Dropdown<T> {
  value: T; // ์–ด๋–ค ํƒ€์ž…์ด๋“  ์˜ฌ ์ˆ˜ ์žˆ์Œ
  title: string; // ๋ฌธ์ž์—ด ํƒ€์ž…์œผ๋กœ ๊ณ ์ •
}

interface DetailedDropdown<T> extends Dropdown<T> {
  description: string;
  tag: T;
  // extends ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋ฏ€๋กœ Dropdown ์ธํ„ฐํŽ˜์ด์Šค์˜ value, title๋„ ๊ฐ€์ง„๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
}

const detailedItem: DetailedDropdown<number> = {
  title: 'a',
  description: 'a',
  value: 'a', // Error
  tag: 'a', // Error
};

 

ํƒ€์ž… ์ถ”๋ก ์„ ํ†ตํ•ด value์˜ ํƒ€์ž…์ด number๊ฐ€ ๋œ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

Best Common Type (๊ฐ€์žฅ ์ ์ ˆํ•œ ํƒ€์ž…)

ํƒ€์ž…์€ ๋ณดํ†ต ๋ช‡ ๊ฐœ์˜(่‹ฅๅนฒ) ํ‘œํ˜„์‹ ์ฝ”๋“œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํƒ€์ž…์„ ์ถ”๋ก ํ•œ๋‹ค. ์ด ํ‘œํ˜„์‹์„ ์ด์šฉํ•˜์—ฌ ๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๋Š”๋ฐ, ์ด ๊ทผ์ ‘ํ•œ ํƒ€์ž…์„ Best Common Type์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

 

์•„๋ž˜ arr ๋ฐฐ์—ด ์š”์†Œ์˜ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„  ๋ฐฐ์—ด์— ์žˆ๋Š” ๊ฐ ์š”์†Œ๋ฅผ ์‚ดํŽด๋ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์˜ ๋ฐฐ์—ด์—์„œ ๊ฐ ์š”์†Œ์˜ ํƒ€์ž…์€ number์™€ null๋กœ ๊ตฌ๋ถ„๋œ๋‹ค. ์ด๋•Œ Best Common Type ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋‹ค๋ฅธ ํƒ€์ž…๋“ค๊ณผ ๊ฐ€์žฅ ์ž˜ ํ˜ธํ™˜ํ•˜๋Š” ํƒ€์ž…์„ ์„ ์ •ํ•˜๋Š” ๊ฒƒ.

let arr = [0, 1, null];

 

๋ฌธ๋งฅ์ƒ์˜ ํƒ€์ดํ•‘

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ์‹์€ ๋ฌธ๋งฅ์œผ๋กœ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๋ฌธ๋งฅ์ƒ์˜ ํƒ€์ดํ•‘(ํƒ€์ž… ๊ฒฐ์ •)์€ ์ฝ”๋“œ์˜ ์œ„์น˜(๋ฌธ๋งฅ)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ผ์–ด๋‚œ๋‹ค.

 

์˜ˆ์ œ 1

์•„๋ž˜ ์ฝ”๋“œ์—์„œ window.onmousedown์— ํ• ๋‹น๋˜๋Š” ํ•จ์ˆ˜์˜ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๊ธฐ ์œ„ํ•ด window.onmousedownํƒ€์ž…์„ ๊ฒ€์‚ฌํ•œ๋‹ค. ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ๋๋‚˜๋ฉด ํ•จ์ˆ˜ ํƒ€์ž…์ด ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ์™€ ๊ด€๋ จ์žˆ๋‹ค๊ณ  ์ถ”๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์— mouseEvent ์ธ์ž์— button ์†์„ฑ์€ ์žˆ์ง€๋งŒ kangaroo ์†์„ฑ์€ ์—†๋‹ค๊ณ  ๊ฒฐ๋ก  ์ง“๋Š”๋‹ค.

window.onmousedown = function (mouseEvent) {
  console.log(mouseEvent.button); // OK
  console.log(mouseEvent.kangaroo); // Error
};

 

์˜ˆ์ œ 2

์˜ˆ์ œ1๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ•จ์ˆ˜๋Š” window.onscroll ์— ํ• ๋‹น๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ ์ธ์ž uiEvent๋Š” UIEvent๋กœ ๊ฐ„์ฃผํ•œ๋‹ค. ๋•Œ๋ฌธ์— button ์†์„ฑ์ด ์—†๋‹ค๊ณ  ์ถ”๋ก ํ•˜์—ฌ uiEvent.button์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ.

window.onscroll = function (uiEvent) {
  console.log(uiEvent.button); // Error!
};

 

์•„๋ž˜์ฒ˜๋Ÿผ ํ•จ์ˆ˜๋ฅผ handler ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๋ฉด, ํ•จ์ˆ˜๊ฐ€ ํ• ๋‹น๋˜๋Š” ๋ณ€์ˆ˜๋งŒ์œผ๋กœ๋Š” ํƒ€์ž…์„ ์ถ”์ •ํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ฌด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฐธ๊ณ ๋กœ noImplicitAny ์˜ต์…˜์„ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

const handler = function (uiEvent) {
  console.log(uiEvent.button); // OK
};

 

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํƒ€์ž… ์ฒดํ‚น

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ํƒ€์ž… ์ฒดํฌ๋Š” ๊ฐ’์˜ ํ˜•ํƒœ์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์ด๋ค„์ ธ์•ผ ํ•œ๋‹ค๊ณ  ์ง€ํ–ฅํ•œ๋‹ค. ์ด๋Ÿฐ๊ฑธ Duck Typing ๋˜๋Š” Structural Subtyping์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

 

  • Duck Typing: ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜ ๋ฐ ๋ฉ”์„œ๋“œ์˜ ์ง‘ํ•ฉ์ด ๊ฐ์ฒด ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ
  • Structural Subtyping: ๊ฐ์ฒด์˜ ์‹ค์ œ ๊ตฌ์กฐ๋‚˜ ์ •์˜์— ๋”ฐ๋ผ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธ

 

ํƒ€์ž… ๋‹จ์–ธ


๐Ÿ’ก ํƒ€์ž… ๋‹จ์–ธ์€ `as` ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ์ถ”๋ก ํ•˜๋Š” ๊ฐ’๋ณด๋‹ค ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ๋” ์ž˜ ์•Œ๊ณ  ์žˆ์„ ๋•Œ ๋ณ€์ˆ˜์— ์›ํ•˜๋Š” ํƒ€์ž…์„ ๊ฐ•์ œ๋กœ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„œ TS๋Š” ๋ณ€์ˆ˜ a๋ฅผ any๋กœ ์ถ”๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€์ˆ˜ b ์—ญ์‹œ any๋กœ ์ถ”๋ก ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„  b๊ฐ€ number 10์ธ๊ฑธ ์•Œ๊ธฐ ๋•Œ๋ฌธ์— as ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ•์ œ๋กœ assertion(๋‹จ์–ธ)ํ•  ์ˆ˜ ์žˆ๋‹ค.

let a;
a = '20';
a = 10; // let a: any
let b = a as number;

 

ํƒ€์ž… ๋‹จ์–ธ์€ querySelector getElementById ๊ฐ™์€ DOM API๋ฅผ ์กฐ์ž‘ํ•  ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ ์ฟผ๋ฆฌ์…€๋ ‰ํ„ฐ๋กœ .container ํด๋ž˜์Šค๋ฅผ ์„ ํƒํ•œ ํ›„ textContext๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๊ฐœ์ฒด๊ฐ€ null์ผ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. querySelector ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์‹œ์ ์—์„œ .container ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๋Š” ๋ณด์žฅ์„ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฐ ๊ฒƒ.

const container = document.querySelector('.container');
container.textContext = 'Hello'; // error: Object is possibly null

 

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” Element๊ฐ€ ์—†์„ ์ƒํ™ฉ์„ ๊ณ ๋ คํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ null ํƒ€์ž…๋„ ๊ฐ™์ด ๋ณด์žฅํ•œ๋‹ค

 

์ด๋Ÿฐ ์ด์œ ๋กœ ๋ณดํ†ต ์•„๋ž˜์ฒ˜๋Ÿผ null์„ ํƒ€์ž…๊ฐ€๋“œ(Element ํƒ€์ž…์ด ์žˆ๋‹ค๊ณ  ๋ณด์žฅ) ์‹œํ‚ค๋Š” ํŒจํ„ด์„ ์‚ฌ์šฉํ•œ๋‹ค.

const container = document.querySelector('.container');
if (container) {
  container.textContext = 'Hello';
}

 

๋งŒ์•ฝ const container... ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์‹œ์ ์— ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๋ฌด์กฐ๊ฑด ์กด์žฌํ•œ๋‹ค๊ณ  ํ™•์‹ ํ•œ๋‹ค๋ฉด as ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด HTMLDivElement๋กœ ํƒ€์ž… ๋‹จ์–ธ์„ ํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿผ null ํƒ€์ž… ๋ณด์žฅ๊ฐ’์€ ์ œ๊ฑฐ๋œ๋‹ค.

const container = document.querySelector('.container') as HTMLDivElement;
container.textContext = 'Hello';

 

ํƒ€์ž… ๊ฐ€๋“œ


๐Ÿ’ก ์œ ๋‹ˆ์˜จ ํƒ€์ž…์˜ ์ธ์ž๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ •ํ™•ํžˆ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ๊ฒ€์‚ฌํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค. ์ด๋•Œ ๊ฐ ํƒ€์ž…์„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•˜์—ฌ ํƒ€์ž…๋ณ„๋กœ ๋กœ์ง์„ ๋ถ„๋ฆฌ(ํƒ€์ž… ๋ฒ”์œ„๋ฅผ ์ขํ˜€์ฃผ๋Š” ์ž‘์—…)ํ•˜๋Š” ํƒ€์ž…๊ฐ€๋“œ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. ํƒ€์ž…๊ฐ€๋“œ๋Š” ํƒ€์ž… ๋‹จ์–ธ์„ ๋” ๊น”๋”ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

 

is ํ‚ค์›Œ๋“œ

ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด ์ง€์ •๋˜๋ฉด ํƒ€์ž…์ด 2๊ฐœ ์ด์ƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ณตํ†ต ์†์„ฑ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

interface Dev {
  name: string;
  skill: string;
}

interface Person {
  name: string;
  age: number;
}

function introduce(): Dev | Person {
  return { name: 'Tony', age: 33, skill: 'JS' };
}

const tony = introduce(); // Dev | Person ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ณตํ†ต ์†์„ฑ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
console.log(tony.skill); // ts(2339) Error (Dev์™€ Person์˜ ๊ณตํ†ต ์†์„ฑ์ธ name๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ)

 

skill ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž… ๋‹จ์–ธ(type assertion)์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹จ์ง€ 2๊ฐœ์˜ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž…๋‹จ์–ธ์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€๋Š” ๋‹จ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค.

// as ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ tony์˜ ํƒ€์ž…์€ Dev๋ผ๊ณ  ๋‹จ์–ธ
if ((tony as Dev).skill) {
  const { skill } = tony as Dev;
  console.log(skill); // 'JS'
  // as ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ tony์˜ ํƒ€์ž…์€ Person๋ผ๊ณ  ๋‹จ์–ธ
} else if ((tony as Person).age) {
  const { age } = tony as Person;
  console.log(age); // 33
}

 

์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ๋ฐ”๋กœ ํƒ€์ž…๊ฐ€๋“œ๋‹ค. ํƒ€์ž…๊ฐ€๋“œ๋Š” ๊ฐ ํƒ€์ž…์„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•˜์—ฌ ํƒ€์ž…๋ณ„๋กœ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ์ž‘์—…(ํƒ€์ž… ๋ฒ”์œ„๋ฅผ ์ขํ˜€์ฃผ๋Š” ์ž‘์—…)์ด๋‹ค. ํƒ€์ž…๊ฐ€๋“œ๋Š” in typeof instanceof ๊ฐ™์€ ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•˜๊ฑฐ๋‚˜, ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํƒ€์ž… ๊ฐ€๋“œ ํ•จ์ˆ˜๋Š” “์–ด๋–ค ์ธ์ž๋Š” ์–ด๋–ค ํƒ€์ž…์ด๋‹ค"๋ผ๋Š” ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜๋กœ ํƒ€์ž…์ด ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ํƒ€์ž… ํŒ๋‹จ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ํƒ€์ž…๊ฐ€๋“œ ํ•จ์ˆ˜๋Š” ๋ฆฌํ„ด ํƒ€์ž…์— parameterName is Type ํ˜•์‹์œผ๋กœ ์ •์˜ํ•˜๋ฉฐ, ํ•จ์ˆ˜๋ช…์€ ๋ณดํ†ต isTypeName ํ˜•ํƒœ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

 

์•„๋ž˜ isDev ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œํ•จ์ˆ˜์˜ ๋‚ด๋ถ€ ๋กœ์ง์„ ํ†ต๊ณผํ•˜๋ฉด ์ธ์ž(target)๋กœ ๋„˜๊ธด ๊ฐ’์˜ ํƒ€์ž…์ด Dev์ธ์ง€(true|false) ์—ฌ๋ถ€๋ฅผ ๊ตฌ๋ถ„ํ•ด์ค€๋‹ค. ์ฆ‰ target ํŒŒ๋ผ๋ฏธํ„ฐ์— skill ์†์„ฑ์ด ์žˆ๋‹ค๋ฉด Dev ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•ด์„œ skill ์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค.

// is ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ํƒ€์ž…๊ฐ€๋“œ ํ•จ์ˆ˜ ์ •์˜(๋„˜๊ฒจ ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•ด๋‹น ํƒ€์ž…์ธ์ง€ ํ™•์ธ)
function isDev(target: Dev | Person): target is Dev {
  // target ๊ฐ์ฒด์— skill ์†์„ฑ์ด ์žˆ๋‹ค๋ฉด Dev ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰(true)
  return (target as Dev).skill !== undefined;
}

if (isDev(tony)) {
  // Dev ์ธํ„ฐํŽ˜์ด์Šค์˜ name, skill ์†์„ฑ ์‚ฌ์šฉ ๊ฐ€๋Šฅ(Dev ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰)
  console.log(tony.skill); // 'JS"
} else {
  // Person ์ธํ„ฐํŽ˜์ด์Šค์˜ name, age ์‚ฌ์šฉ ๊ฐ€๋Šฅ(Person ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰)
  console.log(tony.age); // 33
}

 

typeof ํ‚ค์›Œ๋“œ

typeof๋Š” ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ํ™•์ธํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. typeof ๋ณ€์ˆ˜ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ์™ธ์—, ์ด๋ฏธ ์ •์˜๋˜์–ด ์žˆ๋Š” ๋ณ€์ˆ˜(๊ฐ์ฒด ๋“ฑ) ํƒ€์ž…์„ ์ƒˆ๋กœ ์ •์˜ํ•˜๋Š” ๋ณ€์ˆ˜์— ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ typeofํ‚ค์›Œ๋“œ๋กœ smith ๊ฐ์ฒด ํƒ€์ž…์„ johan ๊ฐ์ฒด๋กœ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒจ์™€์„œ ํ• ๋‹นํ–ˆ๋‹ค. smith ๊ฐ์ฒด์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ ค๋ณด๋ฉด johan ๊ฐ์ฒด์™€ ๋™์ผํ•œ ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

const johan = {
  name: 'John',
  age: 32,
};

const smith: typeof johan = {
  name: 'Smith',
  age: 39,
};

 

 

๋ฆฌ์•กํŠธ ์ปค์Šคํ…€ ํ›…์˜ ๋ฆฌํ„ด ํƒ€์ž…์ด ๋ฐฐ์—ด์ด๊ณ  ๊ฐ ์š”์†Œ๊ฐ€ ๋ชจ๋‘ ๋‹ค๋ฅธ ํƒ€์ž…์ด๋ผ๋ฉด, typeof๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž…์„ ๋” ์‰ฝ๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. (as const๋ฅผ ํ™œ์šฉํ•œ ๋” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋งํฌ ์ฐธ๊ณ )

const useFetchData = (url: string) => {
  // ์ƒ๋žต
  return [loading, data, error] as [typeof loading, typeof data, typeof error];
};

 

in ํ‚ค์›Œ๋“œ

ํƒ€์ž… ๊ฐ€๋“œ์—์„œ ์‚ฌ์šฉ ์˜ˆ์‹œ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ in ์—ฐ์‚ฐ์ž๋กœ ๊ฐ์ฒด key๋ฅผ ์ˆœํšŒํ•˜๋“ฏ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋„ in ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด ํŠน์ • ํƒ€์ž…์ด ๊ฐ€์ง€๋Š” key๋ฅผ ์ˆœํšŒํ•ด์„œ ํƒ€์ž… ๊ฐ€๋“œํ•  ์ˆ˜ ์žˆ๋‹ค.

interface Music {
  title: string;
  genre: string;
}

interface Movie {
  title: string;
  director: number;
}

type Media = Music | Movie;
// Media ํƒ€์ž…์ด ๊ฐ–๋Š” ๊ฐ ์†์„ฑ(Movie/Music)์„ ๊ฐ์ฒด ์•ˆ์—์„œ ์ •์˜ํ•  ๋• ๊ฐ ์†์„ฑ๋งˆ๋‹ค ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž…์ด ์ง€์ •๋œ๋‹ค
// Media ํƒ€์ž…์„ ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•˜์„ ๋• Music/Movie ๊ณตํ†ต ์†์„ฑ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค

 

์•„๋ž˜ printConsole ํ•จ์ˆ˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›๋Š” ์‹œ์ ์— Music Movie ๋‘˜ ์ค‘ ์–ด๋Š ํƒ€์ž…์ด ์˜ฌ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ณตํ†ต ์†์„ฑ๋งŒ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ํƒ€์ž…๋ณ„๋กœ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„  ํƒ€์ž… ๊ฐ€๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ๋Š” in ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด obj ํŒŒ๋ผ๋ฏธํ„ฐ์— rate ์†์„ฑ์ด ์žˆ์œผ๋ฉด Movie ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•˜๊ณ , ์—†์œผ๋ฉด Music ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋„๋ก ํ•˜๋Š” ์˜ˆ์‹œ(๋ฌผ๋ก  is ํ‚ค์›Œ๋“œ ๋“ฑ์„ ํ™œ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค).

const printConsole = (content: Media) => {
  if ('genre' in content) {
    return `this is music of <${content.genre}>`;
  }
  if ('director' in content) {
    return `this is movie of <${content.director}>`;
  }
};

const fkj: Media = {
  title: 'Before Chill',
  genre: 'Jazz Hiphop',
};

printConsole(fkj); // genre ์†์„ฑ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Music ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค

 

๋”๋ณด๊ธฐ

`is` ํ‚ค์›Œ๋“œ ํ™œ์šฉ ์˜ˆ์‹œ

const isMusic = (target: Media): target is Music => {
  return (target as Music).genre !== undefined;
};

const printConsole = (content: Media) => {
  if (isMusic(content)) {
    return `this is music of <${content.genre}>`;
  }
  // ์œ„์—์„œ ์ด๋ฏธ Music ํƒ€์ž…์„ ๋ถ„๊ธฐ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜ ์ฝ”๋“œ๋ถ€ํ„ด Movie ํƒ€์ž…์œผ๋กœ ์ธ์‹ํ•œ๋‹ค
  if (content.director) {
    return `this is movie of <${content.director}>`;
  }
};

 

๋งŒ์•ฝ `is` ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ง€์ €๋ถ„ํ•œ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค.

const printConsole = (content: Media) => {
  if ((content as Music).genre) {
    return `this is music of <${(content as Music).genre}>`;
  }
  
  if ((content as Movie).director)
    return `this is movie of <${(content as Movie).director}>`;
};

 

interface / type์—์„œ ์‚ฌ์šฉ ์˜ˆ์‹œ

์•„๋ž˜์ฒ˜๋Ÿผ type์„ ์ด์šฉํ•ด ํŠน์ • ๊ฐ์ฒด์˜ key ๋ชฉ๋ก์„ ๋ฏธ๋ฆฌ ์ •์˜ํ•œ ํ›„, in ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด ํ•ด๋‹น key ๊ฐ’๋งŒ ๋ฐ›๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๊ด„ํ˜ธ ๋’ค์— ๋ฌผ์Œํ‘œ๋ฅผ ๋ถ™์—ฌ์„œ [key in TypeName]? ์„ ํƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

type NameTypeKeys = 'lastName' | 'firstName';

type NameTypes = {
  [key in NameTypeKeys]: string;
  // type NameTypes = Record<NameTypeKeys, string> ์œ„์™€ ๋™์ผ
};

const johan: NameTypes = {
  lastName: 'Color',
  firstName: 'Filter',
};

 

[...]๋Œ€๊ด„ํ˜ธ ์•ˆ์— ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋Š” computed value๋Š” interface์—์„  ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋Œ€์‹  ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๊ฐ€์ง„ ์†์„ฑ์˜ ํ‚ค ๊ฐ’์ด ๊ฐ์ฒด ํ˜•ํƒœ์ผ๋• ๊ฐ€๋Šฅํ•˜๋‹ค.

type NameTypeKeys = 'lastName' | 'firstName';

interface Students {
  [key: string]: { [key in NameTypeKeys]: string };
  // [key: string]: Record<NameTypeKeys, string> ์œ„์™€ ๋™์ผ
}

const students: Students = {
  colorFilter: {
    lastName: 'Color',
    firstName: 'Filter',
  },
};

 

๐Ÿ’ก ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๊ฐ์ฒด์— `obj[๋ณ€์ˆ˜๋ช…]` ๊ด„ํ˜ธ ํ‘œ๊ธฐ๋ฒ•์„ ์“ฐ๋ฉด, ๊ด„ํ˜ธ์•ˆ์ด number๋‚˜ string์ด ์•„๋‹ˆ๋ฉด ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ’์— ๋Œ€ํ•ด `toString()`์„ ์•”๋ฌต์ ์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„  `toString()`์„ ์•”๋ฌต์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— key ํƒ€์ž…์€ ํ•ญ์ƒ number ํ˜น์€ string์ด์–ด์•ผ ํ•œ๋‹ค.

 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•