[๐Ÿค–] TypeScript ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer ํ‚ค์›Œ๋“œ: ๋ณต์žกํ•œ ํƒ€์ž…๋„ ์†์‰ฝ๊ฒŒ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•

TypeScript ๊ฐœ๋ฐœ์—์„œ ๋งˆ์ฃผํ•˜๋Š” ๋ณต์žกํ•œ ํƒ€์ž… ์ถ”๋ก  ๋ฌธ์ œ, ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer ํ‚ค์›Œ๋“œ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ํ›จ์”ฌ ์šฐ์•„ํ•˜๊ณ  ๊ฐ•๋ ฅํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”. ์‹ค์ „ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๊ทธ ํ™œ์šฉ๋ฒ•์„ ์‹ฌ๋„ ์žˆ๊ฒŒ ๋‹ค๋ค„๋ด…๋‹ˆ๋‹ค.

19๋ถ„
๋‹จ์–ด: 1,786๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

์ด ๊ธ€์—์„œ๋Š” TypeScript์˜ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer ํ‚ค์›Œ๋“œ๋ฅผ ๊นŠ์ด ์žˆ๊ฒŒ ํŒŒ๊ณ ๋“ค์–ด, ๋ณต์žกํ•œ ํƒ€์ž… ์ถ”๋ก  ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ค์šฉ์ ์ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์‹ค์ „ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๋ฐฐ์›Œ๋ด์š”.

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ๊ฒฝ๋ ฅ์˜ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž์ด์ž ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ SEO ์ „๋ฌธ๊ฐ€ ๋ธ”๋ฃจ์˜ˆ์š”. ์ €๋Š” ์‹ค์ œ ์กด์žฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ์žฅ์— ๋„์›€์ด ๋˜๊ณ ์ž ์‹ค๋ฌด ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์œ ์ตํ•œ ๊ธฐ์ˆ  ์ •๋ณด๋ฅผ ๋‚˜๋ˆ„๊ณ  ์žˆ์–ด์š”.

์˜ค๋Š˜์€ TypeScript์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ **์กฐ๊ฑด๋ถ€ ํƒ€์ž…(Conditional Types)**๊ณผ infer ํ‚ค์›Œ๋“œ์— ๋Œ€ํ•ด ์‹ฌ๋„ ์žˆ๊ฒŒ ๋‹ค๋ค„๋ณด๋ ค๊ณ  ํ•ด์š”. ์ด ๋‘ ๊ฐ€์ง€๋Š” ๋ณต์žกํ•œ ํƒ€์ž… ์ถ”๋ก  ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ , ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“ค ๋•Œ ํ•„์ˆ˜์ ์ธ ์š”์†Œ์˜ˆ์š”. ์ดˆ์ค‘๊ธ‰ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค์ด๋ผ๋ฉด ์ด ๊ฐœ๋…๋“ค์„ ํ™•์‹คํžˆ ์ดํ•ดํ•˜๊ณ  ์‹ค๋ฌด์— ์ ์šฉํ•ด ๋ณด์‹œ๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์˜ ํ’ˆ์งˆ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋  ๊ฑฐ์˜ˆ์š”.

0๏ธโƒฃ ๋ณต์žกํ•œ ํƒ€์ž… ์ถ”๋ก ์˜ ํ•„์š”์„ฑ

์šฐ๋ฆฌ๊ฐ€ ๊ฐœ๋ฐœํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ ์  ๋” ๋ณต์žกํ•ด์ง€๊ณ  ์žˆ์–ด์š”. ํŠนํžˆ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋Š” ์„œ๋ฒ„ API ์‘๋‹ต ํ˜•ํƒœ์— ๋”ฐ๋ผ UI๊ฐ€ ๋™์ ์œผ๋กœ ๋ณ€ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋งž์ถฐ ์ปดํฌ๋„ŒํŠธ์˜ ํ”„๋กญ์Šค ํƒ€์ž…์„ ์œ ์—ฐํ•˜๊ฒŒ ์ •์˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์ฃ .
์ด๋•Œ ๋‹จ์ˆœํžˆ string | number | boolean๊ณผ ๊ฐ™์€ ์œ ๋‹ˆ์˜จ ํƒ€์ž…๋งŒ์œผ๋กœ๋Š” ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ค์šด ๋ณต์žกํ•œ ํƒ€์ž… ๊ด€๊ณ„๊ฐ€ ๋ฐœ์ƒํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ์ธ์ž์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๊ฑฐ๋‚˜, ๊ฐ์ฒด ์†์„ฑ์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํƒ€์ž…์„ ์ ์šฉํ•ด์•ผ ํ•  ๋•Œ ๋ง์ด์—์š”.

1๏ธโƒฃ ๊ธฐ์กด ์ œ๋„ค๋ฆญ์˜ ํ•œ๊ณ„

๋ฌผ๋ก  ์ œ๋„ค๋ฆญ(Generics)์„ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์—ฐํ•œ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ ์ œ๋„ค๋ฆญ์€ 'ํƒ€์ž… ๋ณ€์ˆ˜๋ฅผ ๋ฐ›์•„๋“ค์ด๋Š”' ์—ญํ• ์— ๊ทธ์น  ๋ฟ, ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ '๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ ๋ถ„๊ธฐ'ํ•˜๊ฑฐ๋‚˜ 'ํƒ€์ž… ๋‚ด๋ถ€์˜ ํŠน์ • ๋ถ€๋ถ„์„ ์ถ”์ถœ'ํ•˜๋Š” ๋ฐ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์–ด์š”.
์˜ˆ๋ฅผ ๋“ค์–ด, Promise<T> ํƒ€์ž…์—์„œ T๋งŒ ์ถ”์ถœํ•˜๊ณ  ์‹ถ์„ ๋•Œ, ๋‹จ์ˆœํžˆ ์ œ๋„ค๋ฆญ๋งŒ์œผ๋กœ๋Š” ์–ด๋ ต์ฃ . ์ด๋Ÿด ๋•Œ ํ•„์š”ํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer ํ‚ค์›Œ๋“œ๋ž๋‹ˆ๋‹ค.

โš™๏ธ ์กฐ๊ฑด๋ถ€ ํƒ€์ž… (Conditional Types) ํŒŒํ—ค์น˜๊ธฐ

์กฐ๊ฑด๋ถ€ ํƒ€์ž…์€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ '์กฐ๊ฑด'์— ๋”ฐ๋ผ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”. JavaScript์˜ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž์™€ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ•์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”.

0๏ธโƒฃ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•๊ณผ ๋™์ž‘ ์›๋ฆฌ

์กฐ๊ฑด๋ถ€ ํƒ€์ž…์˜ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”:

type MyConditionalType<T> = T extends U ? X : Y;

์—ฌ๊ธฐ์„œ T extends U๋Š” T๊ฐ€ U์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์ธ์ง€(subtype์ธ์ง€) ๊ฒ€์‚ฌํ•˜๋Š” ์กฐ๊ฑด์ด์—์š”.

  • ๋งŒ์•ฝ ์กฐ๊ฑด์ด ์ฐธ(true)์ด๋ฉด X ํƒ€์ž…์ด ์„ ํƒ๋˜๊ณ ,
  • ๊ฑฐ์ง“(false)์ด๋ฉด Y ํƒ€์ž…์ด ์„ ํƒ๋ผ์š”.

๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณผ๊นŒ์š”?

type IsString<T> = T extends string ? "yes" : "no"; type A = IsString<string>; // type A = "yes" type B = IsString<number>; // type B = "no" type C = IsString<any>; // type C = "yes" | "no" (any๋Š” ๋ชจ๋“  ํƒ€์ž…์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด ๋ผ์š”) type D = IsString<"hello">; // type D = "yes"

์œ„ ์ฝ”๋“œ์—์„œ IsString<T>๋Š” T๊ฐ€ string ํƒ€์ž…์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ์ง€ ๊ฒ€์‚ฌํ•ด์š”. string ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ธ "hello"๋„ string์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ "yes"๊ฐ€ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

1๏ธโƒฃ extends ํ‚ค์›Œ๋“œ์˜ ์—ญํ• 

์กฐ๊ฑด๋ถ€ ํƒ€์ž…์—์„œ extends๋Š” ๋‹จ์ˆœํžˆ ์ƒ์† ๊ด€๊ณ„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, **ํƒ€์ž… ํ˜ธํ™˜์„ฑ(Type Compatibility)**์„ ๊ฒ€์‚ฌํ•˜๋Š” ์—ญํ• ์„ ํ•ด์š”. ์ฆ‰, ์ขŒ๋ณ€์˜ ํƒ€์ž…์ด ์šฐ๋ณ€์˜ ํƒ€์ž…์— 'ํ• ๋‹น ๊ฐ€๋Šฅํ•œ์ง€'๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด์ฃ .

์˜ˆ๋ฅผ ๋“ค์–ด, string extends string์€ true์ด๊ณ , "hello" extends string๋„ true์˜ˆ์š”. ํ•˜์ง€๋งŒ number extends string์€ false๊ฒ ์ฃ .

๊ฐ์ฒด ํƒ€์ž…์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์˜ˆ์š”:

interface Animal { name: string; } interface Dog extends Animal { breed: string; } type CheckAnimal<T> = T extends Animal ? "Is Animal" : "Not Animal"; type E = CheckAnimal<Dog>; // type E = "Is Animal" (Dog๋Š” Animal์— ํ• ๋‹น ๊ฐ€๋Šฅ) type F = CheckAnimal<{ name: string; age: number }>; // type F = "Is Animal" (๊ตฌ์กฐ์ ์œผ๋กœ Animal์— ํ• ๋‹น ๊ฐ€๋Šฅ) type G = CheckAnimal<{ age: number }>; // type G = "Not Animal"

Dog๋Š” Animal์„ ์ƒ์†ํ•˜๋ฏ€๋กœ ๋‹น์—ฐํžˆ "Is Animal"์ด์—์š”. ํฅ๋ฏธ๋กœ์šด ์ ์€ F ํƒ€์ž…์—์„œ ๋ณด๋“ฏ์ด, ๋ช…์‹œ์ ์œผ๋กœ ์ƒ์†๋ฐ›์ง€ ์•Š์•„๋„ Animal์˜ ๋ชจ๋“  ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด "Is Animal"๋กœ ํŒ๋‹จ๋œ๋‹ค๋Š” ์ ์ด์—์š”. ์ด๋Š” TypeScript์˜ ๊ตฌ์กฐ์  ํƒ€์ดํ•‘(Structural Typing) ๋•๋ถ„์ด์ฃ .

์กฐ๊ฑด๋ถ€ ํƒ€์ž…๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•ด์š”. ์กฐ๊ฑด์ด ์ฐธ์ผ ๋•Œ, ๊ทธ ์กฐ๊ฑด ์†์—์„œ '์ถ”๋ก ๋œ ํƒ€์ž…'์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ๊ฑฐ๋“ ์š”. ์ด๋•Œ ๋“ฑ์žฅํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ infer ํ‚ค์›Œ๋“œ์˜ˆ์š”.

0๏ธโƒฃ infer์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

infer๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์˜ extends ์ ˆ ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํŠน๋ณ„ํ•œ ํ‚ค์›Œ๋“œ์˜ˆ์š”. ํŠน์ • ์œ„์น˜์˜ ํƒ€์ž…์„ '์ถ”๋ก (infer)'ํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž… ๋ณ€์ˆ˜๋กœ ๋„์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜์š”.

๋ฌธ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”:

type MyInferType<T> = T extends SomeType<infer U> ? U : never;

์—ฌ๊ธฐ์„œ SomeType<infer U>๋Š” T๊ฐ€ SomeType ํ˜•ํƒœ์ด๊ณ , ๊ทธ ์•ˆ์— U๋ผ๋Š” ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ๊ทธ ์ถ”๋ก ๋œ U๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์˜ˆ์š”. ๋งŒ์•ฝ ์ถ”๋ก ์— ์‹คํŒจํ•˜๋ฉด never ํƒ€์ž…์ด ๋ฐ˜ํ™˜๋˜๊ฒ ์ฃ .

1๏ธโƒฃ ์‹ค์ œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ๊ตฌํ˜„ ์‚ฌ๋ก€ (ReturnType, Parameters)

TypeScript ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์ค‘ ReturnType๊ณผ Parameters๊ฐ€ infer๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ์˜ˆ์š”.

ReturnType<T>: ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž… ์ถ”์ถœํ•˜๊ธฐ

ReturnType<T>๋Š” ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด์—์š”. ๋‚ด๋ถ€์ ์œผ๋กœ infer๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณผ๊นŒ์š”?

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function greet(name: string): string { return `Hello, ${name}!`; } function add(a: number, b: number): number { return a + b; } type GreetReturnType = MyReturnType<typeof greet>; // type GreetReturnType = string type AddReturnType = MyReturnType<typeof add>; // type AddReturnType = number type NotAFunction = MyReturnType<string>; // type NotAFunction = any (ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ)

MyReturnType์„ ๋ณด๋ฉด, T extends (...args: any[]) => infer R ? R : any; ๋ผ๋Š” ์กฐ๊ฑด์ด ์žˆ์–ด์š”.

  • T๊ฐ€ '์–ด๋–ค ์ธ์ž(...args: any[])๋ฅผ ๋ฐ›๊ณ  R์ด๋ผ๋Š” ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜'์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฉด,
  • ๊ทธ ๋ฐ˜ํ™˜ ํƒ€์ž… R์„ infer๋กœ ์ถ”๋ก ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ ,
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด any๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋˜์–ด์žˆ์ฃ . ์•„์ฃผ ์œ ์šฉํ•ด์š”!

Parameters<T>: ํ•จ์ˆ˜์˜ ์ธ์ž ํƒ€์ž… ํŠœํ”Œ ์ถ”์ถœํ•˜๊ธฐ

Parameters<T>๋Š” ํ•จ์ˆ˜์˜ ์ธ์ž ํƒ€์ž…์„ ํŠœํ”Œ๋กœ ์ถ”์ถœํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด์—์š”. ์ด๊ฒƒ๋„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณผ๊นŒ์š”?

type MyParameters<T> = T extends (...args: infer P) => any ? P : never; function multiply(a: number, b: number): number { return a * b; } type MultiplyParams = MyParameters<typeof multiply>; // type MultiplyParams = [a: number, b: number] class Calculator { add(x: number, y: number): number { return x + y; } } type CalculatorAddParams = MyParameters<Calculator['add']>; // type CalculatorAddParams = [x: number, y: number] type NotAFunctionParams = MyParameters<number>; // type NotAFunctionParams = never

MyParameters๋Š” T extends (...args: infer P) => any ? P : never; ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•ด์š”.

  • T๊ฐ€ '์–ด๋–ค ์ธ์ž(...args: infer P)๋ฅผ ๋ฐ›๊ณ  any ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜'์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฉด,
  • ๊ทธ ์ธ์ž๋“ค์˜ ํƒ€์ž… ํŠœํ”Œ P๋ฅผ infer๋กœ ์ถ”๋ก ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ ,
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด never๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋˜์–ด์žˆ์–ด์š”. ์ •๋ง ๋ฉ‹์ง€์ง€ ์•Š๋‚˜์š”?

2๏ธโƒฃ infer๋ฅผ ํ™œ์šฉํ•œ ๋” ๋ณต์žกํ•œ ํƒ€์ž… ์ถ”์ถœ

infer๋Š” ๋‹จ์ˆœํžˆ ํ•จ์ˆ˜์˜ ์ธ์ž๋‚˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์—์„œ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐฐ์—ด์˜ ์š”์†Œ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, Promise์˜ ๋น„๋™๊ธฐ ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๋Š” ๋“ฑ ๋ง์ด์—์š”.

type ElementType<T> = T extends (infer U)[] ? U : T; type Arr = ElementType<string[]>; // type Arr = string type Str = ElementType<string>; // type Str = string (๋ฐฐ์—ด์ด ์•„๋‹ˆ๋ฏ€๋กœ T ์ž์ฒด๊ฐ€ ๋ฐ˜ํ™˜) type PromiseValue<T> = T extends Promise<infer U> ? U : T; type P1 = PromiseValue<Promise<string>>; // type P1 = string type P2 = PromiseValue<Promise<number[]>>; // type P2 = number[] type P3 = PromiseValue<string>; // type P3 = string

ElementType๋Š” T extends (infer U)[] ์กฐ๊ฑด์„ ํ†ตํ•ด T๊ฐ€ ๋ฐฐ์—ด์ด๋ผ๋ฉด ๊ทธ ์š”์†Œ ํƒ€์ž… U๋ฅผ ์ถ”์ถœํ•˜๊ณ , PromiseValue๋Š” T extends Promise<infer U>๋ฅผ ํ†ตํ•ด Promise์˜ ์ œ๋„ค๋ฆญ ํƒ€์ž… U๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ์ด์ฒ˜๋Ÿผ infer๋Š” ํƒ€์ž… ๋‚ด๋ถ€์˜ 'ํŒจํ„ด'์„ ์ธ์‹ํ•˜๊ณ  ๊ทธ ํŒจํ„ด์— ๋งž๋Š” ํƒ€์ž…์„ ๋ฝ‘์•„๋‚ผ ๋•Œ ๊ฐ•๋ ฅํ•œ ํž˜์„ ๋ฐœํœ˜ํ•ด์š”.

์ด์ œ ์‹ค์ œ ๊ฐœ๋ฐœ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ infer์™€ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ํ™œ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.

0๏ธโƒฃ Promise ๊ฒฐ๊ณผ ํƒ€์ž… ์ถ”์ถœํ•˜๊ธฐ (UnwrapPromise)

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ๋‹ค๋ฃฐ ๋•Œ, Promise<T>์—์„œ ์‹ค์ œ ๊ฒฐ๊ณผ ํƒ€์ž… T๋งŒ ๊น”๋”ํ•˜๊ฒŒ ์ถ”์ถœํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ๋งŽ์•„์š”. PromiseValue์™€ ๋น„์Šทํ•˜์ง€๋งŒ, ์ค‘์ฒฉ๋œ Promise๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ํ•ด๋ณผ๊นŒ์š”?

type UnwrapPromise<T> = T extends Promise<infer U> ? U extends Promise<any> ? UnwrapPromise<U> : U : T; interface User { id: number; name: string; } async function fetchUser(id: number): Promise<User> { return { id, name: `User ${id}` }; } async function fetchUsersWithDelay(): Promise<Promise<User[]>> { return new Promise(resolve => { setTimeout(() => resolve(Promise.resolve([{ id: 1, name: 'Alice' }])), 1000); }); } type FetchedUser = UnwrapPromise<ReturnType<typeof fetchUser>>; // type FetchedUser = User type DelayedUsers = UnwrapPromise<ReturnType<typeof fetchUsersWithDelay>>; // type DelayedUsers = User[] type JustString = UnwrapPromise<string>; // type JustString = string

UnwrapPromise๋Š” ์žฌ๊ท€์ ์œผ๋กœ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์„œ ์ค‘์ฒฉ๋œ Promise๋„ ๋ชจ๋‘ ํ’€์–ด๋‚ด๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. U extends Promise<any> ? UnwrapPromise<U> : U ๋ถ€๋ถ„์ด ํ•ต์‹ฌ์ธ๋ฐ์š”, ์ถ”๋ก ๋œ U๊ฐ€ ๋˜ Promise๋ผ๋ฉด UnwrapPromise๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์—ฌ ํƒ€์ž…์„ ํ•œ ๊ฒน ๋” ๋ฒ—๊ฒจ๋‚ด๋Š” ๋ฐฉ์‹์ด์—์š”. ์ •๋ง ์œ ์šฉํ•˜์ฃ !

1๏ธโƒฃ ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ ํƒ€์ž… ๋‹ค๋ฃจ๊ธฐ

TypeScript์—์„œ ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ์€ ๊ฐ™์€ ์ด๋ฆ„์˜ ํ•จ์ˆ˜์— ์—ฌ๋Ÿฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ๊ธฐ๋Šฅ์ด์—์š”. infer์™€ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์€ ์ด๋Ÿฌํ•œ ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐ๋„ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์–ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์˜ค๋ฒ„๋กœ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜์—์„œ ํŠน์ • ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•ด์š”. ํ•˜์ง€๋งŒ infer๋Š” ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜์— ์ ์šฉ๋  ๋•Œ, ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์ •์˜๋œ(๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ) ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ถ”๋ก ํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์–ด์š”. ๋”ฐ๋ผ์„œ ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜ ์ „์ฒด์˜ ํƒ€์ž…์„ ๋‹ค๋ฃจ๊ธฐ๋ณด๋‹ค๋Š”, ๊ฐ ์˜ค๋ฒ„๋กœ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ช…์‹œํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์„ ํƒํ•˜์—ฌ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹์ด ๋” ํšจ๊ณผ์ ์ผ ์ˆ˜ ์žˆ์–ด์š”.

์—ฌ๊ธฐ์„œ๋Š” infer๊ฐ€ ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜์—์„œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด๊ณ , ์ฃผ์˜ํ•  ์ ์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”.

function createLogger(prefix: string): (message: string) => void; function createLogger(level: 'info' | 'warn', message: string): void; function createLogger(arg1: string | 'info' | 'warn', arg2?: string): ((message: string) => void) | void { if (typeof arg1 === 'string' && arg2 === undefined) { const prefix = arg1; return (message: string) => console.log(`[${prefix}] ${message}`); } else if (typeof arg1 === 'string' && typeof arg2 === 'string') { const level = arg1; const message = arg2; console.log(`[${level.toUpperCase()}] ${message}`); } } type LoggerReturnType = ReturnType<typeof createLogger>; // type LoggerReturnType = ((message: string) => void) | void

ReturnType<typeof createLogger>๋ฅผ ๋ณด๋ฉด, ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜ createLogger์˜ ๋ชจ๋“  ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์ข…ํ•ฉํ•œ ์œ ๋‹ˆ์˜จ ํƒ€์ž… ((message: string) => void) | void๊ฐ€ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.
์ด๋Š” infer๊ฐ€ ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜ ํƒ€์ž…์— ์ ์šฉ๋  ๋•Œ, ๋ชจ๋“  ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ํฌ๊ด„ํ•˜๋Š” '์ตœ์ข… ๊ตฌํ˜„ ์‹œ๊ทธ๋‹ˆ์ฒ˜' ๋˜๋Š” '๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‹œ๊ทธ๋‹ˆ์ฒ˜'๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ถ”๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์—์š”. ํŠน์ • ์˜ค๋ฒ„๋กœ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋งŒ ์ •ํ™•ํžˆ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์€ ๋” ๋ณต์žกํ•œ ํƒ€์ž… ์กฐ์ž‘์ด ํ•„์š”ํ•˜๋ฉฐ, ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์˜ค๋ฒ„๋กœ๋”ฉ๋œ ํ•จ์ˆ˜ ์ž์ฒด์˜ ํƒ€์ž…์„ ๋‹ค๋ฃจ๊ธฐ๋ณด๋‹ค, ํ˜ธ์ถœ ์‹œ์ ์— ํƒ€์ž… ์ถ”๋ก ์„ ๋งก๊ธฐ๊ฑฐ๋‚˜ ๊ฐ ์˜ค๋ฒ„๋กœ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ณ„๋„์˜ ํƒ€์ž…์œผ๋กœ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์‹ค์šฉ์ ์ผ ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ

0๏ธโƒฃ ํ•ต์‹ฌ ์š”์•ฝ

์˜ค๋Š˜์€ TypeScript์˜ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer ํ‚ค์›Œ๋“œ์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„๋ดค์–ด์š”.

  • ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์€ T extends U ? X : Y ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ํƒ€์ž…์„ ๋ถ„๊ธฐํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์—์š”.
  • infer ํ‚ค์›Œ๋“œ๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์˜ extends ์ ˆ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋ฉฐ, ํƒ€์ž… ๋‚ด๋ถ€์˜ ํŠน์ • ๋ถ€๋ถ„์„ '์ถ”๋ก 'ํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž… ๋ณ€์ˆ˜๋กœ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜์š”.
  • ์ด ๋‘ ๊ฐ€์ง€๋ฅผ ์กฐํ•ฉํ•˜๋ฉด ReturnType, Parameters์™€ ๊ฐ™์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ ๋ฌผ๋ก , UnwrapPromise์ฒ˜๋Ÿผ ๋ณต์žกํ•œ ํƒ€์ž… ๋ณ€ํ™˜ ๋กœ์ง๋„ ์šฐ์•„ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

์ด ๊ฐœ๋…๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ๋ถ„์˜ TypeScript ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๋” ๊ฒฌ๊ณ ํ•˜๊ณ  ์œ ์—ฐํ•ด์งˆ ๊ฑฐ์˜ˆ์š”. ํŠนํžˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ์ด๋‚˜ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋กœ์ง์—์„œ ๊ทธ ์ง„๊ฐ€๋ฅผ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ์–ด์š”.

1๏ธโƒฃ ๋‹ค์Œ ์Šคํ…: ๋” ๊นŠ์ด ํƒ๊ตฌํ•ด ๋ณผ๊นŒ์š”?

์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ infer๋Š” TypeScript์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ด์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ๋งˆ์ฃผํ•˜๋Š” ๋‹ค์–‘ํ•œ ํƒ€์ž… ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋  ๊ฑฐ์˜ˆ์š”. ์˜ค๋Š˜ ๋‹ค๋ฃฌ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ์— ์ง์ ‘ ์ ์šฉํ•ด ๋ณด์‹œ๋ฉด์„œ ์ต์ˆ™ํ•ด์ง€๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”.
๋” ๋‚˜์•„๊ฐ€์„œ๋Š” ๋ถ„์‚ฐ ์œ ๋‹ˆ์˜จ ํƒ€์ž…(Distributive Conditional Types)์ด๋‚˜ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…๊ณผ์˜ ์กฐํ•ฉ์„ ํ†ตํ•ด ๋”์šฑ ๊ฐ•๋ ฅํ•˜๊ณ  ์ •๊ตํ•œ ํƒ€์ž… ์กฐ์ž‘์„ ์‹œ๋„ํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ต๋‹ˆ๋‹ค.
๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ์ถ”๊ฐ€๋กœ ๋‹ค๋ฃจ๊ณ  ์‹ถ์€ ์ฃผ์ œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ์žฅ์„ ์‘์›ํ•ด์š”!

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ