ํ์ ์คํฌ๋ฆฝํธ์์ ํ์ ์ ์ขํ๋ ๋ฐฉ๋ฒ
์ค๋ฌด์์ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ 1๋ ์ ๋ ๋ค๋ฃจ๋ฉฐ ๋ ๊น์ ์ดํด์ ํ์์ฑ์ ๋๋ผ๋ ์ฐธ์ '์ดํํฐ๋ธ ํ์ ์คํฌ๋ฆฝํธ'๋ผ๋ ์ฑ ์ ์ฝ๊ฒ ๋๊ณ ์ค์ฉ์ ์ธ ๋ด์ฉ์ด ๋ง์ ์ ๋ฆฌํด๋๋ ค๊ณ ํฉ๋๋ค.
ํ์ ์คํฌ๋ฆฝํธ์ ์ ๋์จ ์ฐ์ฐ์๋ฅผ ํ์ฉํ๋ฉด ์ฌ๋ฌ ํ์ ์ ํฉ์งํฉ์ ์ ์ํ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋๊ฐ์์ ์ํ๋ ํ์ ์ธ์ง ํ์ธํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค. ์ด๋ฅผ 'ํ์ ์ขํ๊ธฐ(narrowing)'๋ผ ํฉ๋๋ค.
์ด ๊ธ์๋ ํ์ ์ ์ขํ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ๋ฆฌํ๊ณ ์ค๋ฌด์์ ํ๊ทธ๋ ์ ๋์จ(tagged union)์ ํ์ฉํด ๊ฐ์ ํ๋ ์ฌ๋ก๋ฅผ ๋ด์์ต๋๋ค.
ํ์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ์ ํน์ง
ํ์ ์คํฌ๋ฆฝํธ์ ํ์ ์ '์ ๊ฑฐ ๊ฐ๋ฅ(erasable)'ํฉ๋๋ค. ์ค์ ๋ก ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ปดํ์ผ๋๋ ๊ณผ์ ์์ ๋ชจ๋ ์ธํฐํ์ด์ค, ํ์ , ํ์ ๊ตฌ๋ฌธ์ ๊ทธ๋ฅ ์ ๊ฑฐ๋์ด ๋ฒ๋ฆฝ๋๋ค.
โ ์์ดํ 3. ์ฝ๋ ์์ฑ๊ณผ ํ์ ์ด ๊ด๊ณ์์์ ์ดํดํ๊ธฐ
ํ์ ์คํฌ๋ฆฝํธ๋ ํ๋ก์ ํธ์ ์ค์ ๋ ๋ฒ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ํธ๋์คํ์ผ1๋ฉ๋๋ค. ๋ณํ๋ ๊ฒฐ๊ณผ๋ฌผ์ด ์๋ฐ์คํฌ๋ฆฝํธ๋ผ๋ ๊ฑด ์ฐ๋ฆฌ๊ฐ ์์ฑํ ํ์ ๊ณผ ๊ด๋ จ๋ ๊ตฌ๋ฌธ๋ค์ด ์ ๊ฑฐ๋๋ค๋ ๋ป์ ๋๋ค.
์ฌ๊ธฐ์ ๊ถ๊ธํ ์ ์ด ์๊น๋๋ค. ๋ง์ฝ ์ ๋์จ์ผ๋ก ์ ์๋ ํ์ ์ ์ ๋ฌ ๋ฐ๋ ํจ์๊ฐ ์๋ค๋ฉด ํ์ ์ด ์ ๊ฑฐ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฐํ์์์ ์ด๋ป๊ฒ ํน์ ํ์ ์ธ ๊ฒ์ ์ด๋ป๊ฒ ๋ณด์ฅํ ์ ์์๊น์? ์ฐ์ ํ์ ์ด ์ ๊ฑฐ๋ ๊ฒฝ์ฐ ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
ํ์ ์ด ์ ์๋ ์ฝ๋๋ ๋ฌธ์ ๊ฐ ๋๋ ๋ถ๋ถ์ ์์ฑ ๋จ๊ณ์์ ์ ํํ ์ฐพ์๋ ๋๋ค.
interface Dog { run: VoidFunction; } interface Bird { fly: VoidFunction; } function runAway(animal: Dog | Bird) { animal.run(); // ~~~ Property 'run' does not exist on type 'Bird'. }
ํ์ง๋ง ์๋์ ๊ฐ์ ํ์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ ํน์ฑ์ผ๋ก ์ธํด ์ฌ๊ฐํ ๋ฒ๊ทธ๋ฅผ ์ง๋ ์ ์์ต๋๋ค.
ํ์ ์ค๋ฅ๊ฐ ์กด์ฌํ๋๋ผ๋ ์ฝ๋ ์์ฑ(์ปดํ์ผ)์ ๊ฐ๋ฅํฉ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ํ์์ ๋ฐํ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
โ ์์ดํ 3. ์ฝ๋ ์์ฑ๊ณผ ํ์ ์ด ๊ด๊ณ์์์ ์ดํดํ๊ธฐ
๋ง์ฝ tsconfig.json ํ์ผ์ noEmitError๋ผ๋ ์ต์ ์ ํ์ฑํํ์ง ์์ผ๋ฉด ํ์ ์๋ฌ์ ๋ฌด๊ดํ๊ฒ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ณํ๋ ์ ์์ต๋๋ค. ๊ทธ ๊ฒฐ๊ณผ ๋ฐํ์์์ ์๋์ ๊ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค.
function runAway(animal) { animal.run(); } const animal = { fly: () => console.log(`Let's fly!`), }; runAway(animal) // Uncaught TypeError: animal.run is not a function
ํน์ ํ์ ์ ๋ณด์ฅํ๋ ค๋ฉด ๋ฐํ์ ์ฝ๋์์๋ ํ์ ์ ์ขํ ๋ฐฉ์์ด ํ์ํฉ๋๋ค.
null
, instanceof
, typeof
, ์์ฑ ์ฒดํฌ
์๋ฐ์คํฌ๋ฆฝํธ ๋ณ์์ ํ์ ๊ณผ ๋ฌธ๋ฒ์ ํ์ฉํด ์ ๋ฌ๋ ๊ฐ์ ์ฒดํฌํ๋ ๋น๊ต์ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๋๋ค.
const button = document.getElementById('button'); // HTMLElement | null // falsy ๊ฐ ์ฒดํฌ๋ก ํ์ ์ขํ๊ธฐ if (button) { button // HTMLElement } else { button // null } // ์ธ์คํด์ค ์ฌ๋ถ ์ฒดํฌ๋ก ํ์ ์ขํ๊ธฐ function printTime(date: number | Date) { if (date instanceof Date) { date // Date } else { date // number } } // typeof ์ฒดํฌ๋ก ํ์ ์ขํ๊ธฐ function stringify(value: number | string) { if (typeof value === 'string') { value // string } else { value // number } } // ์์ฑ ์ ๋ฌด ์ฒดํฌ๋ก ํ์ ์ขํ๊ธฐ interface Dog { run: VoidFunction; } interface Bird { fly: VoidFunction; } function runAway(animal: Dog | Bird) { if ('run' in animal) { animal // Dog } if ('fly' in animal) { animal // Bird } }
์ด ๊ธ์ ์ด๋ฐ๋ถ์์ ๋์๋ ์ ๋์จ ํ์ ์ ์ขํ๋ ๋ฌธ์ ๋ ์์ฑ ์ฒดํฌ๋ก ์ฝ๊ฒ ํด๊ฒฐ์ด ๊ฐ๋ฅํด ๋ณด์ ๋๋ค. ํ์ง๋ง ์ค๋ฌด์์ ์ด ๋ฐฉ์์ ์ ์ฉํ๊ธฐ ์ด๋ ค์ด ์ฌ๋ก๊ฐ ์์์ต๋๋ค.
๋ฆฌ๋ชจํธ์์ ์ ๊ณต๋๋ ์ฌ๋ฌ ์ ํ์ ๋ฐ์ดํฐ๊ฐ ๊ฑฐ์ ์ ์ฌํ UI๋ก ์ ๊ณต๋๋ ์ผ์ด์ค์ธ๋ฐ์, ์๋ฅผ ๋ค์ด๋ณด๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
type ArticleA = { field1: string; field2: string; author: string; }; type ArticleB = { field2: string; field3: string; author: string; }; type ArticleC = { field3: string; field4: string; author: string; };
React๋ก ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํ์ ๋ ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ธฐ ์ํด ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
type Props = { content: ArticleA | ArticleB | ArticleC; }; const Article: FC<Props> = ({ content }) => { return ( <article> <p>{content.author}</p> </article> ); };
์ด ์์ ๋ถํฐ ๊ณ ๋ฏผ์ด ๋ฉ๋๋ค. ๊ณตํต๋ ํ๋์ธ author
๋ ๋ณ๋ค๋ฅธ ์กฐ๊ฑด ์์ด ๋ฐ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. (์ ๋์จ ์ฐ์ฐ์ ํน์ฑ์ ์ด์ ๊ธ์์ ์ค๋ช
๋ ๋ฐ ์์ต๋๋ค)
ํ์ง๋ง field1
๋ถํฐ field4
๋ ๊ณตํต๋ ํ๋๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ํ์
์ขํ๊ธฐ๊ฐ ํ์ํฉ๋๋ค. ์์ ๋์จ ๋ฐฉ๋ฒ ์ค ์์ฑ ์ฒดํฌ๊ฐ ๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฆ
๋๋ค.
type Props = { content: ArticleA | ArticleB | ArticleC; }; const Article: FC<Props> = ({ content }) => { if ('field1' in content) { content // ArticleA } if ('field2' in content) { content // ArticleA | ArticleB } return (...); };
ArticleA
์๋ง ์กด์ฌํ๋ field1
์ ์ฝ๊ฒ ํ์
์ขํ์ง๋๋ค. ๋ฐ๋ฉด ๋ ์ธํฐํ์ด์ค์ ์กด์ฌํ๋ field2
๋ ์ขํ ๋ฐฉ์์ด ๋ง๋
์น ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฆฌ๋ชจํธ ๋ฐ์ดํฐ ํน์ฑ์ ํ๋์ ๋ณ๋ ๊ฐ๋ฅ์ฑ๋ ๋ฐฐ์ ํ ์ ์์ต๋๋ค.
์ด๋ฌํ ์ํฉ์์ ์ ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ช ์์ ํ๊ทธ๋ฅผ ๋ถ์ด๋ ๊ฒ์ ๋๋ค.
ํ๊ทธ๋ ์ ๋์จ (tagged union)
ํ๊ทธ๋ ์ ๋์จ ๋๋ ๊ตฌ๋ณ๋ ์ ๋์จ(discriminated union)์ด๋ผ ๋ถ๋ฆฌ๋ ์ด ๋ฐฉ๋ฒ์ ํ์ ์ ์๋ณํ๊ธฐ ์ํด ๋ช ์์ 'ํ๊ทธ'๋ฅผ ๋ถ์ด๋ ๊ฒ์ ๋๋ค. ์์ ๋์๋ ๊ฐ ํ์ ์ ์๋ณํ ์ ์๋ ํ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
type ArticleA = { kind: 'a'; field1: string; field2: string; author: string; }; type ArticleB = { kind: 'b'; field2: string; field3: string; author: string; }; type ArticleC = { kind: 'c'; field3: string; field4: string; author: string; };
์ด์ kind
ํ๋๋ฅผ ํ์ฉํ๋ฉด ๊ฐ ํ์
์ ํ๋ ๋ณํ์ ์ํฅ์ ๋ฐ์ง ์๊ณ ๊ตฌ๋ถํ ์ ์์ต๋๋ค.
type Props = { content: ArticleA | ArticleB | ArticleC; }; const Article: FC<Props> = ({ content }) => { if (content.kind === 'a') { content // ArticleA } if (content.kind === 'b') { content // ArticleB } return (...); };
์ด๋ป๊ฒ ๋ณด๋ฉด ๋จ์ํด ๋ณด์ด์ง๋ง ์๋ฐ์คํฌ๋ฆฝํธ ๋ฐํ์์๋ ํ์ ์ ๋ณด์ฅํ ์ ์๋ ์ ์ฉํ ๋ฐฉ๋ฒ์ ๋๋ค.
์ฌ์ฉ์ ์ ์ ํ์ ๊ฐ๋ (user-defined type guard)
๋๋ก๋ ํ์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ๋ณด๋ค ์ฝ๋ ์์ฑ์๊ฐ ํ์ ์ ๋ ์ ํํ ์๊ณ ์๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. DOM๊ณผ ๊ด๋ จ๋ ์ฒ๋ฆฌ๊ฐ ๋ํ์ ์ธ ์์์ ๋๋ค.
ํ์ ์คํฌ๋ฆฝํธ์์๋ ์ถ์ํ๋ ์์ค์ ๋ฐ๋ผ DOM ๊ณ์ธต์ ํ์ ๋ค์ด ๊ตฌ๋ถ๋ฉ๋๋ค. ์๋ ํ๋ ๊ณ์ธต ๊ตฌ์กฐ์ ์์์ ๋๋ค.
ํ์ | ์์ |
---|---|
EventTarget | window, XMLHttpRequest |
Node | document, Text, Comment |
Element | HTMLElement, SVGElement ํฌํจ |
HTMLElement | <small> , <strong> |
HTMLButtonElement | <button> |
์๋ธํ์ ๊ด๊ณ๊ฐ ๋ค์ํ๊ธฐ ๋๋ฌธ์ DOM ๊ด๋ จ ์ฝ๋๋ฅผ ์์ฑํ ๋๋ ํ์ ์ ๊ตฌ์ฒดํํ๊ธฐ ์ํ ๋จ์ธ๋ฌธ์ ์ฌ์ฉํ๊ฑฐ๋ ์๋์ ๊ฐ์ ์ฌ์ฉ์ ์ ์ ํ์ ๊ฐ๋๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
function isInputElement(el: HTMLElement): el is HTMLInputElement { return 'value' in el; } function getElementContent(el: HTMLElement) { if (isInputElement(el)) { el // HTMLInputElement } else { el // HTMLElement } }
๋ง๋ฌด๋ฆฌ
๋ถ๊ธฐ๋ฌธ ์ธ์๋ ์ฌ๋ฌ ์ข ๋ฅ์ ์ ์ด ํ๋ฆ์ ์ดํด๋ณด๋ฉฐ ํ์ ์คํฌ๋ฆฝํธ๊ฐ ํ์ ์ ์ขํ๋ ๊ณผ์ ์ ์ดํดํด์ผ ํฉ๋๋ค.
โ ์์ดํ 22. ํ์ ์ขํ๊ธฐ
ํ์
์ด ์ขํ์ง๋ ๊ณผ์ ์ ํ์ธํ ๋ ๊ฐ๋จํ ์ฝ๋๋ TypeScript Playground ์ฌ์ดํธ๋ฅผ ํ์ฉํ๋ฉด ์ ์ฉํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ VSCode์ ๊ฐ์ด ํ์
์คํฌ๋ฆฝํธ ์ง์์ด ์๋๋ IDE์์๋ ์ปค์๋ง ์ฌ๋ ค๋ ํ์
์ ๋ณด๊ฐ ๋์ค๊ณ , ํ์
์ ์๋ฅผ ์ฝ๊ฒ ์ถ์ ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๋๊ตฌ๋ฅผ ํ์ฉํด ์ฝ๋๋ฅผ ์์ฑํ๊ณ ๋ก์ง์ ์์๊ฐ๋ ๊ณผ์ ์์ ์ค๊ฐ์ค๊ฐ ํ์
์ด ์ด๋ป๊ฒ ์ถ๋ก ๋๊ณ ์๋์ง ์์๋ก ํ์ธํด์ผ ๋์ค์ ํ์
๋๋ฒ๊น
์ ์ํ ๋น์ฉ์ ์ค์ผ ์ ์์ ๊ฒ์
๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ํฉ์ ๋ฐ๋ผ ๋ฐํ์
๋ถ๊ธฐ ๋ฌธ๋ฒ๊ณผ ํ๊ทธ๋ ์ ๋์จ, ์ฌ์ฉ์ ์ ์ ํ์
๊ฐ๋ ๋ฑ์ ๊ธฐ๋ฒ์ ์ ํ์ฉํด์ผ๊ฒ ์ต๋๋ค.
Footnotes
-
ํธ๋์คํ์ผ(translate + compile)์ ํ๋์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก ์์ฑ๋ ํ๋ก๊ทธ๋จ์ ์์ค ์ฝ๋๋ฅผ ์ ๋ ฅ์ผ๋ก ๋ฐ์ ๋ค๋ฅธ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก ๋๋ฑํ ์์ค ์ฝ๋๋ฅผ ๋ง๋ค์ด๋ด๋ ํ์๋ฅผ ์๋ฏธํฉ๋๋ค. โฉ