πŸ” ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ FE λ°λΈŒμ½”μŠ€ 5κΈ° TIL - TypeScript μ œλ„€λ¦­, μ‘°κ±΄λΆ€νƒ€μž…, infer

Jun 2k (Jun2)Β·2023λ…„ 11μ›” 14일
1

2023. 11. 14

πŸ’» Intro & TMI

이제 1μ°¨ νŒ€μ›κ³Όμ˜ μ‹œκ°„μ΄ ν•˜λ£¨λ°–μ— 남지 μ•Šμ•˜λ‹€...
λ°λΈŒμ½”μŠ€μ—μ„œ 처음 λ§Œλ‚œ μ‚¬λžŒλ“€μ΄κΈ°λ„ ν•˜κ³  μš°μ—¬κ³‘μ ˆμ΄ λ§Žμ•„ 정이 λ“€μ—ˆλ˜ νŒ€μ΄μ—ˆλ˜ ν˜„μ£ΌνŒ€μΈλ° 벌써 ν—€μ–΄μ§ˆ μ‹œκ°„μ΄λΌλ‹ˆ.
μ‹œκ°„μ΄ μ—„μ²­ 빨리 μ§€λ‚˜κ°€λŠ” κ±° κ°™λ‹€.
이번 μ£ΌλŠ” κ°•μ˜λ„ μ›”, ν™”λ‘œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό λ§ˆλ¬΄λ¦¬ν•˜κ³  λ‹€μŒμ£Ό μ›”μš”μΌκΉŒμ§€λŠ” 방학이라 μž¬μ •λΉ„ μ‹œκ°„μ΄λ‹€.
λ°©ν•™ λ™μ•ˆμ—λŠ” ν˜„μ£ΌνŒ€κ³Ό μ§„ν–‰ν•˜λ €κ³  ν–ˆλ˜ λ―Έλ‹ˆκ²Œμž„μ²œκ΅­ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈλ₯Ό λ§ˆλ¬΄λ¦¬ν•˜κ³  λΆ€μ‘±ν•œ 곡뢀λ₯Ό 더 ν•  것이닀.



🧐 였늘 μƒˆλ‘­κ²Œ 배운 것

μ œλ„€λ¦­

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜ μ˜€λ²„λ‘œλ”©μ„ ν†΅ν•΄μ„œ νƒ€μž… 쑰건을 μΆ”κ°€ν•˜λ‹€κ°€ 보면 μ•„λž˜ κ·Έλ¦Όκ³Ό 같이 μ½”λ“œκ°€ μ—„μ²­ κΈΈμ–΄μ§€λŠ” κ²½μš°κ°€ λ°œμƒν•œλ‹€.

μ΄λŸ¬ν•œ λΉ„νš¨μœ¨μ μΈ μ½”λ“œ μž‘μ„±μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œ μ‚¬μš©ν•  수 μžˆλŠ” κ°œλ…μ΄ μ œλ„€λ¦­(Generic)이닀.

λ³΅μž‘ν•œ νƒ€μž… 쑰건이 λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜κ°’μ— λ™μΌν•˜κ²Œ 적용될 경우 이 νƒ€μž… 쑰건을 λ³€μˆ˜ν™”ν•˜μ—¬ μ μš©ν•˜λŠ” 것이닀.

μ œλ„€λ¦­μ€ <> κ΄„ν˜Έλ₯Ό 톡해 μ„ μ–Έ 및 ν˜ΈμΆœν•  수 μžˆλ‹€.
μ œλ„€λ¦­μ˜ 이름을 자유둭게 μ„€μ • κ°€λŠ₯ν•˜λ‹€.

function toObj<T>(a: T, b: T): { a: T; b: T } {
  return { a, b }
}

toObj<string>('A', 'B')
toObj<number>(1, 2)
toObj<boolean>(true, false)

μ œλ„€λ¦­μ€ νƒ€μž… 좔둠에 μ˜ν•΄ 호좜 μ‹œ μ œλ„€λ¦­μ„ μƒλž΅ν•˜λ”λΌλ„ 맀개 λ³€μˆ˜κ°€ μ§€μ •λœ κ²ƒμœΌλ‘œ 좔둠이 κ°€λŠ₯ν•˜λ©΄ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
λ°˜λŒ€λ‘œ 좔둠을 톡해 μ˜¬λ°”λ₯΄μ§€ μ•Šμ€ νƒ€μž…μ΄ μ§€μ •λ˜μ—ˆμœΌλ©΄ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

function toObj<T>(a: T, b: T): { a: T; b: T } {
  return { a, b }
}

// 'A'λ₯Ό 톡해 aκ°€ string νƒ€μž… 및 Tκ°€ string νƒ€μž…μž„μ„ μ•Œ 수 μžˆμ–΄
// μ œλ„€λ¦­μ„ μƒλž΅ν•΄λ„ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
toObj('A', 'B')

// 'A'λ₯Ό 톡해 μ œλ„€λ¦­ Tκ°€ string인데 b의 νƒ€μž…μ΄ numberμ΄λ―€λ‘œ
// μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.
toObj('A', 123) // 'number' ν˜•μ‹μ˜ μΈμˆ˜λŠ” 'string' ν˜•μ‹μ˜ 맀개 λ³€μˆ˜μ— 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ μœ„μ˜ toObj의 ν•¨μˆ˜μ˜ T νƒ€μž…μ—λŠ” nullμ΄λ‚˜ undefined νƒ€μž…μ΄ 듀어가도 μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•Šκ²Œ λ˜λŠ” 치λͺ…적인 λ¬Έμ œκ°€ μžˆλ‹€.

λ”°λΌμ„œ μ œλ„€λ¦­μ€ μ œμ•½μ‘°κ±΄κ³Ό 함꼐 μ‚¬μš©λ˜μ–΄μ•Ό μ΄λŸ¬ν•œ 문제λ₯Ό λ°©μ§€ν•  수 μžˆλ‹€. μ œμ•½μ‘°κ±΄μ€ extends둜 μ„€μ •ν•œλ‹€.

function toObj<T extends string | number | boolean>(
  a: T,
  b: T,
): { a: T; b: T } {
  return { a, b }
}

μ œμ•½μ‘°κ±΄μ„ 톡해 T νƒ€μž…μ˜ 쑰건을 λͺ…μ‹œμ μœΌλ‘œ μ •ν•΄μ€˜μ„œ μ‚¬μš©μžκ°€ μ›ν•˜λŠ” νƒ€μž…μ΄ μ•„λ‹Œ 것은 μ‚¬μš©ν•˜μ§€ μ•Šλ„λ‘ μ œμ•½μ„ κ±Έμ–΄μ•Ό ν•œλ‹€.

μ œλ„€λ¦­μ€ ν•¨μˆ˜ 말고도 μΈν„°νŽ˜μ΄μŠ€, ν΄λž˜μŠ€μ—λ„ μ‚¬μš© κ°€λŠ₯ν•˜λ‹€!
μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 μœ„ toObj ν•¨μˆ˜μ˜ λ°˜ν™˜κ°’ νƒ€μž…μ„ μ’€ 더 κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€ 수 μžˆλ‹€.

interface ToObj<T> {
  a: T
  b: T
}

function toObj<T extends string | number | boolean>(a: T, b: T): ToObj<T> {
  return { a, b }
}

μ—¬λŸ¬ 개의 μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ μ•„λž˜μ™€ 같이 μ‚¬μš© κ°€λŠ₯ν•˜λ‹€.

/*
  interface User<T, U, V> {
    name: T,
    age: U,
    isValid: V
  }
*/

type User<T, U, V> = { name: T; age: U; isValid: V } | [T, U, V]

type U = User<string, number, boolean>

const person1: U = { name: 'person1', age: 17, isValid: true }

const person2: U = ['person2', 19, false]

보톡 객체 νƒ€μž…μœΌλ‘œ νƒ€μž…μ„ μ§€μ •ν•  λ•ŒλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•  수 μžˆλŠ”λ° person2처럼 μ§€μ •ν•˜κ³  싢은 νƒ€μž…μ΄ μ œλ„€λ¦­μ„ μš”μ†Œλ‘œ κ°€μ§€λŠ” λ°°μ—΄ ν˜•νƒœλΌλ©΄ type μ—°μ‚°μžλ₯Ό 톡해 νŠœν”Œ ν˜•νƒœλ‘œ μ§€μ •ν•  수 μžˆλ‹€.

λ˜ν•œ νƒ€μž… 별칭 Uμ—μ„œ μ‚¬μš©ν•œ κ²ƒμ²˜λŸΌ νƒ€μž… 별칭에도 μ œλ„€λ¦­μ„ ν¬ν•¨μ‹œν‚¬ 수 있으며 μ΄λ ‡κ²Œ μž‘μ„±ν•˜λ©΄ μ½”λ“œκ°€ κ°„κ²°ν•΄μ§„λ‹€.


μ œλ„€λ¦­μ„ ν™œμš©ν•œ 쑰건뢀 νƒ€μž…

νƒ€μž…μ„ μ‚¬μš©μžκ°€ μ›ν•˜λŠ” 쑰건에 따라 μ§€μ •ν•  수 μžˆλ„λ‘ JS의 μ‚Όν•­μ—°μ‚°μž λ¬Έλ²•μ²˜λŸΌ κ΅¬ν˜„ν•  수 μžˆλ‹€.
μ΄λ ‡κ²Œ κ΅¬ν˜„ν•œ 쑰건뢀 νƒ€μž…μ„ μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄λΌκ³ λ„ λΆ€λ₯Έλ‹€.

κ°•μ˜μ—μ„œλŠ” μ—¬λŸ¬ κ°€μ§€ 예제λ₯Ό λ§Œλ‚˜λ΄€λŠ”λ° κ½€ μœ μš©ν–ˆλ‹€.

// νƒ€μž…μ΄ string λ˜λŠ” number일 λ•Œλ§Œ boolean νƒ€μž…μ„ μ§€μ •ν•˜λŠ” 쑰건뢀 νƒ€μž…
type MyType<T> = T extends string | number ? boolean : never

const a: MyType<string> = true
const b: MyType<number> = true
const c: MyType<null> = true // μ—λŸ¬: 'boolean' ν˜•μ‹μ€ 'never' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

λ‚΄κ°€ μ›ν•˜λŠ” νƒ€μž…μ— ν•΄λ‹Ήν•  λ•Œλ§Œ νŠΉμ •ν•œ νƒ€μž…μ„ μ§€μ •ν•  수 μžˆλŠ” ν•„ν„° 역할을 ν•˜λŠ” MyTypeμ΄λΌλŠ” μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄μ—ˆλ‹€.


// TλΌλŠ” νƒ€μž…μ—μ„œ U의 νƒ€μž…μ„ μ œμ™Έν•˜λŠ” 쑰건뢀 νƒ€μž…
// T νƒ€μž…μ΄ μ œμ•½μ‘°κ±΄μΈ U νƒ€μž…κ³Ό μΌμΉ˜ν•˜λ©΄ never νƒ€μž…μ„ 톡해 할당을 λ§‰λŠ”λ‹€.
type MyExclude<T, U> = T extends U ? never : T
type MyUnion = string | number | boolean | null

const a: MyExclude<MyUnion, boolean | null> = 123

const b: MyExclude<MyUnion, boolean | null> = null
// 'null' ν˜•μ‹μ€ 'string | number' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

λ§ˆμ°¬κ°€μ§€λ‘œ Union νƒ€μž…μ΄ μžˆμ„ λ•Œ ν•΄λ‹Ή νƒ€μž…μ—μ„œ νŠΉμ •ν•œ νƒ€μž…μ„ μ œμ™Έν•  수 μžˆλŠ” MyExclude μœ ν‹Έλ¦¬ν‹° νƒ€μž…μ΄λ‹€.


// TλΌλŠ” 객체 νƒ€μž…μ— νŠΉμ • μ†μ„±μ˜ νƒ€μž…μ„ μ²΄ν¬ν•˜λŠ” 쑰건뢀 νƒ€μž…μ΄λ‹€.
type IsPropertyType<T, U extends keyof T, V> = T[U] extends V ? true : false
// keyof ν‚€μ›Œλ“œλ₯Ό 톡해 객체 νƒ€μž…μ˜ 속성(ν‚€)λ₯Ό μΆ”μΆœν•  수 μžˆλ‹€.
type Keys = keyof User // 'name' | 'age'

interface User {
  name: string
  age: number
}

const n: IsPropertyType<User, 'name', number> = true
// User 객체 νƒ€μž…μ˜ 'name' 속성은 string νƒ€μž…μ΄λ―€λ‘œ
// false νƒ€μž…μ΄ μ§€μ •λ˜λ―€λ‘œ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

7μ£Όμ°¨ 고양이 사진첩 κ³Όμ œμ—μ„œ 객체 νƒ€μž… λ‚΄ μ†μ„±μ˜ νƒ€μž…μ„ κ²€μ‚¬ν•˜λŠ” λ‘œμ§μ„ κ΅¬ν˜„ν–ˆμ—ˆλ‹€.
이것을 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ 쑰건뢀 νƒ€μž…μ„ 톡해 μ†μ‰½κ²Œ κ΅¬ν˜„ν•  수 μžˆμ—ˆλ‹€.
νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” ꢁ극적인 μ΄μœ κ°€ 이것이지 μ•Šμ„κΉŒ

  • keyof
    νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ 객체 νƒ€μž… λ˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ—μ„œ ν‚€(속성)을 Union νƒ€μž…μœΌλ‘œ 뽑아낼 수 있게 ν•΄μ€€λ‹€.

infer ν‚€μ›Œλ“œ

infer ν‚€μ›Œλ“œλŠ” μ œμ•½ 쑰건을 μ„€μ •ν•  λ•Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—κ²Œ νƒ€μž… 좔둠을 ν•  λŒ€μƒμ„ μ§€μ •ν•  λ•Œ 쓰인닀.

μ•„λž˜ μ½”λ“œλŠ” λ°°μ—΄ νƒ€μž…μ΄ μ£Όμ–΄μ§€λ©΄ ν•΄λ‹Ή λ°°μ—΄ νƒ€μž… λ‚΄ μš”μ†Œμ˜ νƒ€μž…μ΄ μΌμΉ˜ν•  λ•Œλ§Œ 값을 ν• λ‹Ήν•  수 있게 ν•˜λŠ” 쑰건뢀 νƒ€μž…μ΄λ‹€.

type ArrayItemType<T> = T extends (infer I)[] ? I : never

const numbers = [1, 2, 3]
const a: ArrayItemType<number[]> = 123
const b: ArrayItemType<boolean> = 123
// 'number' ν˜•μ‹μ€ 'never' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

const fruits = ['Apple', 'Banana', 'Cherry']
const c: ArrayItemType<typeof fruits> = 'Mango'

const hello = () => {}
const d: ArrayItemType<typeof hello> = 'hello'
// 'string' ν˜•μ‹μ€ 'never' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

ArrayItemTypeμ—μ„œ μ£Όμ–΄μ§„ Tνƒ€μž…κ³Ό μ œμ•½μ‘°κ±΄μ„ 비ꡐ할 λ•Œ (infer I)[]을 μ§€μ •ν•˜λ©΄ λ°°μ—΄ νƒ€μž…μ΄κΈ΄ ν•œλ° λ°°μ—΄ μ•ˆ μš”μ†Œμ˜ νƒ€μž…μ„ T νƒ€μž…μ„ λ°”νƒ•μœΌλ‘œ μΆ”λ‘ ν•  수 있게 ν•΄μ£ΌλŠ” 것이 infer ν‚€μ›Œλ“œμ΄λ‹€.

μΆ”λ‘  과정을 μ •λ¦¬ν•˜λ©΄

  1. aμ—μ„œλŠ” Tνƒ€μž…μ΄ μš”μ†Œ νƒ€μž…μ΄ number인 배열을 μ§€μ •ν–ˆμœΌλ―€λ‘œ μ œμ•½μ‘°κ±΄κ³Ό 비ꡐ할 λ•Œ Iκ°€ number라고 좔둠이 κ°€λŠ₯ν•˜λ‹€. λ”°λΌμ„œ number인 Iλ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— 123을 ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€.

  2. bμ—μ„œ Tλ₯Ό boolean νƒ€μž…μœΌλ‘œ μ§€μ •ν–ˆμ„ λ•ŒλŠ” μ• μ΄ˆμ— λ°°μ—΄ νƒ€μž…μ΄ μ•„λ‹ˆλ―€λ‘œ μΆ”λ‘ ν•  μˆ˜κ°€ μ—†λ‹€. λ”°λΌμ„œ never νƒ€μž…μ΄ λ°˜ν™˜λ˜λ―€λ‘œ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

  3. cμ—μ„œλŠ” typeof fruitsκ°€ string[]이 λ˜λ―€λ‘œ IλŠ” string으둜 μΆ”λ‘ λœλ‹€. λ”°λΌμ„œ Mangoλ₯Ό ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€.

  4. dμ—μ„œλŠ” typeof helloκ°€ () => Void둜 ν•΄μ„λ˜λ―€λ‘œ μ• μ΄ˆμ— λ°°μ—΄ νƒ€μž…μ΄ μ•„λ‹ˆκΈ°μ— never νƒ€μž…μ΄ λ°˜ν™˜λœλ‹€. λ”°λΌμ„œ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

이와 같이 μ œμ•½μ‘°κ±΄μ„ 비ꡐ할 λ•Œ inferλ₯Ό μ§€μ •ν•˜λ©΄ μΆ”λ‘ ν•  λŒ€μƒμ„ ꡬ체적으둜 μ„€μ •ν•  수 μžˆλ‹€.


μ•„λž˜ SecondArgumentType νƒ€μž…μ€ Tνƒ€μž…μ˜ 두 번째 인자의 νƒ€μž…μ„ μ²΄ν¬ν•œλ‹€.

type SecondArgumentType<T> = T extends (f: any, s: infer S) => any ? S : never

function hello(a: string, b: number) {}
const a: SecondArgumentType<typeof hello> = 123

μ—¬κΈ°μ„œ f와 λ°˜ν™˜κ°’μ„ any νƒ€μž…μœΌλ‘œ μ§€μ •ν•œ μ΄μœ λŠ” μ‚¬μš©μžκ°€ κΆκΈˆν•˜κ³  μ§‘μ€‘ν•˜λŠ” λŒ€μƒμ€ 두 번째 인자인 s의 νƒ€μž…μ΄λ―€λ‘œ λ‚˜λ¨Έμ§€λŠ” μ–΄λ–€ νƒ€μž…μ΄ 듀어와도 되기 λ•Œλ¬Έμ΄λ‹€.

λ”°λΌμ„œ hello ν•¨μˆ˜μ˜ 두 번째 인자 bκ°€ number νƒ€μž…μ΄λ―€λ‘œ aλŠ” number νƒ€μž…μ΄ λ˜λ―€λ‘œ 123을 ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€.



πŸ‘€ λŠλ‚€μ 

πŸ‘ Keep

이전 CSS κ°•μ˜ λ•Œ κ°•μ˜λ₯Ό 듣고도 과제 λ•Œ ν™œμš©μ„ λͺ»ν–ˆλ˜ κ²½ν—˜μ΄ μžˆμ–΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈ κ°•μ˜λΆ€ν„°λŠ” λ‹€μ‹œ 백문이 λΆˆμ—¬μΌνƒ€λ₯Ό μ μš©ν–ˆλ‹€. 직접 μ½”λ“œλ₯Ό 쳐보고 이것 저것을 λ³€κ²½ν•΄λ³΄λ©΄μ„œ μ½”λ“œκ°€ 이해될 λ•ŒκΉŒμ§€ λœ―μ–΄λ³΄λ‹ˆ κ°œλ… 이해가 잘 λ˜λŠ” 것 κ°™λ‹€.

😱 Problem

νƒ€μž…μŠ€ν¬λ¦½νŠΈ μŠ€ν„°λ””λ₯Ό λ”°λ‘œ μ§„ν–‰ν–ˆλŠ”λ° 첫 회의λ₯Ό λΆˆμ°Έν–ˆλ‹€. μ΄μœ λŠ” μ „λ‚  λ„ˆλ¬΄ 늦게 μžμ„œ λŠ¦μž μ„ μžλ²„λ¦° 것이닀. μ»¨λ””μ…˜ 및 ν•™μŠ΅ 일정 관리λ₯Ό 더 μ² μ €νžˆ ν•˜μž...

😜 Try

νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό κ³΅λΆ€ν•˜λ‹€λ³΄λ‹ˆ 7μ£Όμ°¨ 과제 λ•Œ μƒνƒœμ— λŒ€ν•œ νƒ€μž… 검사λ₯Ό 꼼꼼히 ν–ˆμ—ˆμ–΄μ„œ κ·ΈλŸ°κ°€ 이해가 μ’€ λΉ¨λžλ‹€. μ–Όλ₯Έ ν”„λ‘œμ νŠΈλ‚˜ ν¬νŠΈν΄λ¦¬μ˜€μ— μ μš©ν•΄λ΄μ•Όκ² λ‹€.



πŸ˜… ν•΄λ‹Ή λ‚΄μš©μ€ κ³΅λΆ€ν•˜λ©΄μ„œ μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€. ν‹€λ¦° λΆ€λΆ„μ΄λ‚˜ μ˜€ν•΄ν•˜κ³  μžˆλŠ” 뢀뢄이 μžˆλ‹€λ©΄ ν”Όλ“œλ°± λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.

profile
ν”„λ‘ νŠΈμ—”λ“œ 개발자 쀀비쀑...

0개의 λŒ“κΈ€