[TS] TypeScript ํ์ ์คํฌ๋ฆฝํธ Infer ํค์๋ ํ์ฉํ๊ธฐ
ํ์
์คํฌ๋ฆฝํธ infer๋ ํ์
์ ์ถ๋ก ํ๋๋ฐ ์ฌ์ฉํ๋ ํค์๋๋ค. ๋ฐฐ์ด ์์ ํ์
, ํจ์ ํ๋ผ๋ฏธํฐ ํ์
, ํจ์ ๋ฐํ ํ์
 ๋ฑ ํน์  ๋ฌธ๋งฅ์ ํ์
 ์ถ์ถ์ด ํ์ํ  ๋ infer๋ฅผ ํ์ฉํ  ์ ์๋ค.
๊ธฐ๋ณธ ๋ฌธ๋ฒ :
T extends infer U ? U : T—U๊ฐ ์ถ๋ก ๊ฐ๋ฅํ ํ์ ์ด๋ฉดU๋ฆฌํด, ์๋๋ฉดT๋ฆฌํด
infer๋ ํ์
 ๊ตฌํ ์์ญ(ํ ๋น ์ฐ์ฐ์ ์ฐ์ธก)์ ์กฐ๊ฑด๋ถ ํ์
(extends ์ )์์๋ง ์ฌ์ฉํ  ์ ์์ผ๋ฉฐ, TS ์์ง์ด ํ์
์ ์ถ๋ก ํ๊ณ  ๋ณ์(์ ๋ค๋ฆญ ํ์
)์ ํ ๋นํ๋ค. ํ ๋น๋ ๋ณ์๋ ์กฐ๊ฑด๋ถ ํ์
์ true ๋ถ๊ธฐ์์๋ง ์ฌ์ฉํ  ์ ์๋ค.
// Error! 'infer' declarations are only permitted in the 'extends' clause of a conditional type.(ts1338)
type Wrong1<T extends (infer U)[]> = T[0]; // ์ ์ฝ ์กฐ๊ฑด extends์ ์ฌ์ฉํด์ ์๋ฌ
type Wrong2<T> = (infer U)[] extends T ? U : T; // extends ์กฐ๊ฑด์  ์ฌ์ฉํ์ง ์์์ ์๋ฌ
// Error! Cannot find name 'U'.(ts2304)
type Wrong3<T> = T extends (infer U)[] ? T : U; // false ๋ถ๊ธฐ์ ์ฌ์ฉํด์ ์๋ฌ
UnpackedArray
๋ฐฐ์ด ์์ ํ์ ์ถ์ถ
T๊ฐ ๋ฐฐ์ด์ด๊ณ , ๋ฐฐ์ด ์์์ ํ์
์ ์ถ๋ก ํ  ์ ์์ผ๋ฉด U(์ ๋ค๋ฆญ ํ์
) ๋ณ์์ ํ ๋นํ๋ค. โผ
type UnpackedArray<T> = T extends (infer U)[] ? U : T;
type T0 = string[];
type U0 = UnpackedArray<T0>; // string

UnpackedFn
ํจ์ ๋ฐํ ํ์ ์ถ์ถ
T๊ฐ ํจ์์ด๊ณ , ํจ์์ ๋ฐํ ํ์
์ ์ถ๋ก ํ  ์ ์์ผ๋ฉด ๋ณ์(์ ๋ค๋ฆญ ํ์
) U์ ํ ๋นํ๋ค. โผ
type UnpackedFn<T> = T extends (...args: unknown[]) => infer U ? U : T;
type T1 = () => string;
type U1 = UnpackedFn<T1>; // string
ํจ์๋ฅผ ์ค๋ฒ๋ก๋ํ๋ค๋ฉด ๋ง์ง๋ง ํธ์ถ ์๊ทธ๋์ฒ๋ฅผ ํ์ ์ถ๋ก ์ ์ฌ์ฉํ๋ค. ์ฐธ๊ณ ๋ก ์๊ทธ๋์ฒ(Signiture)๋ ํจ์๋ ๋ฉ์๋์ ์ ๋ ฅ(ํ๋ผ๋ฏธํฐ)๊ณผ ์ถ๋ ฅ(๋ฐํ๊ฐ)์ ๋ํ ํ์ ์ ๋ณด๋ฅผ ์๋ฏธํ๋ค.
declare function foo(x: string): number;
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;
type U2 = UnpackedFn<typeof foo>; // string | number

Unpacked
๋ฐฐ์ด ์์ ํ์ / ํจ์ ๋ฐํ ํ์ / ํ๋ก๋ฏธ์ค ๊ฒฐ๊ณผ ํ์ ์ถ์ถ
T๊ฐ ๋ฐฐ์ด | ํจ์ | ํ๋ก๋ฏธ์ค๊ฐ ์๋๋ฉด T๋ฅผ ๊ทธ๋๋ก ๋ฐํํ๋ค. โผ
type Unpacked<T> =
    T extends (infer U)[] ? U :
    T extends (...args: any[]) => infer U ? U :
    T extends Promise<infer U> ? U :
    T; // ์ ๋ฌ๋ฐ์ T ๊ทธ๋๋ก ๋ฐํ
type T0 = Unpacked<string>;  // string
type T1 = Unpacked<string[]>;  // string
type T2 = Unpacked<() => string>;  // string
type T3 = Unpacked<Promise<string>>;  // string
type T4 = Unpacked<Promise<string>[]>;  // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>;  // string
PropertyType
๊ฐ์ฒด ํค ํ์ ์ถ์ถ
T๊ฐ ๊ฐ์ฒด์ด๊ณ , ๊ฐ์ฒด ํค ํ์
์ ์ถ๋ก ํ  ์ ์์ผ๋ฉด ๋ณ์(์ ๋ค๋ฆญ ํ์
) U, R์ ํ ๋นํ๋ค. โผ
type User = { id: number; name: string };
type PropertyType<T> = T extends { id: infer U; name: infer R } ? [U, R] : T;
type U3 = PropertyType<User>; // [number, string]
๋ง์ฝ ์ ๋ค๋ฆญ ํ์
 U๋ฅผ 1๊ฐ๋ง ์ ์ธํ๋ฉด ์ ๋์จ ํ์
์ ๋ฐํํ๋ค. ๊ณต๋ณ ์์น(ํจ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ธํ ๊ณณ)์์ ๋์ผํ ํ์
 ๋ณ์(U)์ ๋ํ ํ๋ณด๊ฐ ์ฌ๋ฌ๊ฐ๋ฉด ์ต์ข
 ํ์
์ ์ ๋์จ ํ์
์ผ๋ก ์ ์ถํ๊ธฐ ๋๋ฌธ์ด๋ค.
type PropertyType<T> = T extends { id: infer U; name: infer U } ? U : T;
type U4 = PropertyType<User>; // string | number
๋ฐ๋ฉด, ๋ฐ๊ณต๋ณ ์์น(ํจ์ ํ๋ผ๋ฏธํฐ)์์ ๋์ผํ ํ์
 ๋ณ์(U)์ ๋ํ ํ๋ณด๊ฐ ์ฌ๋ฌ๊ฐ๋ฉด ์ต์ข
 ํ์
์ ์ธํฐ์น์
 ํ์
์ผ๋ก ์ ์ถํ๋ค. ๋๋ฌธ์ ์๋ ์์์์ U๋ string & number ํ์
์ด ๋๋ค. ํ์ง๋ง ์ ๋ค๋ฆญ์ผ๋ก ๋๊ธด ๋ ๋ฉ์๋์ x ํ๋ผ๋ฏธํฐ ํ์
๊ณผ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ false ๋ถ๊ธฐ๋ฅผ ํ์ never๋ฅผ ๋ฐํํ๋ค. (T extends U ? X : Y ์กฐ๊ฑด๋ถ ํ์
 ๋ฌธ๋ฒ์์ T๋ฅผ U์ ํ ๋นํ  ์ ์์ผ๋ฉด true ๋ถ๊ธฐ๋ฅผ, ํ ๋นํ  ์ ์์ผ๋ฉด false ๋ถ๊ธฐ๋ฅผ ํ๋ค)
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
  ? U
  : never;
  
type U5 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // never
๋ ์์ธํ ๋ด์ฉ์ ํ์ ์คํฌ๋ฆฝํธ์ ๊ณต๋ณ์ฑ / ๋ฐ๊ณต๋ณ์ฑ / ์ด๋ณ์ฑ ํฌ์คํ ์ฐธ๊ณ
- ๊ณต๋ณ์ฑ : ์ํผ ํ์
(๋์ ํ์
)์ด ์๋ธ ํ์
(์ข์ ํ์
)์ ์ปค๋ฒํ๋ ๊ด๊ณ
- ์๋ธ ํ์ ์ ์ํผ ํ์ ์ ๋์ ํ ์ ์์ e.g. superType = subType
 - ํ์ ์คํฌ๋ฆฝํธ ๊ธฐ๋ณธ ๋์
 
 - ๋ฐ๊ณต๋ณ์ฑ : ์๋ธ ํ์
(์ข์ ํ์
)์ด ์ํผ ํ์
(๋์ ํ์
)์ ์ปค๋ฒํ๋ ๊ด๊ณ 
- ์ํผ ํ์ ์ ์๋ธ ํ์ ์ ๋์ ํ ์ ์์ e.g. subType = superType
 - ํจ์ ํ๋ผ๋ฏธํฐ ๋์
 
 - ์ด๋ณ์ฑ : ๊ณต๋ณ์ฑ๊ณผ ๋ฐ๊ณต๋ณ์ฑ์ ๋ชจ๋ ํฌํจํ๋ ๊ด๊ณ 
- ์ํผ ํ์ ๊ณผ ์๋ธ ํ์ ์ ๋ชจ๋ ๋์ ํ ์ ์์
 strictFunctionTypes์ต์ ์ ๊ป์ ๋ ํจ์ ํ๋ผ๋ฏธํฐ ๋์
 
FirstIfString
๋ฐฐ์ด์ ์ฒซ๋ฒ์งธ ์์๊ฐ ๋ฌธ์์ด ํ์ ์ด๋ฉด ์ถ์ถ
์๋ ์์์ ์ฒซ๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
์์  ์ ๋ค๋ฆญ์ผ๋ก ๋ฐ์ T๊ฐ ๋ฐฐ์ด์ธ์ง ํ์ธํ๊ณ , ๋ฐฐ์ด์ ์ฒซ๋ฒ์งธ ์์๋ฅผ ํ์
 ๋ณ์ S์ ํ ๋นํ๋ค. ๋๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
์์  ํ ๋น๋ ํ์
 ๋ณ์ S๊ฐ ๋ฌธ์์ด์ธ์ง ํ์ธํ๊ณ  ์ฐธ์ด๋ฉด S๋ฅผ ๋ฐํํ๋ค.
type FirstIfString<T> = T extends [infer S, ...unknown[]] // ์ฒซ๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
  ? S extends string // ๋๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
    ? S
    : never
  : never;
type A = FirstIfString<[string, number, number]>; // string
type B = FirstIfString<['a', number, number]>; // 'a'
type C = FirstIfString<['a | b', number]>; // 'a' | 'b'
ํ์
์คํฌ๋ฆฝํธ 4.7 ๋ฒ์ ๋ถํฐ infer ํ์
์ extends ์ ์ ์ฌ์ฉํด์ ์ ์ฝ ์กฐ๊ฑด์ ์ถ๊ฐํ  ์ ์๋ค. ์ ์์์์  2๊ฐ์ ์กฐ๊ฑด๋ถ ํ์
์ ์ฌ์ฉํ๋๋ฐ, ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด 1๊ฐ์ ์กฐ๊ฑด๋ถ ํ์
๋ง ์ฌ์ฉํด์ ์์ฑํ  ์ ์๋ค.
// infer์ extends๋ฅผ ์ฌ์ฉํด์ ํ์
 ๋ณ์ S๋ฅผ ๋ฌธ์์ด ํ์
์ผ๋ก ์ ํํ๊ณ  ์๋ค
type FirstIfString<T> = T extends [infer S extends string, ...unknown[]]
  ? S
  : never;
UnionToIntersection
์ ๋์จ ํ์ ์ ๋ฐ์ ์ธํฐ์น์  ํ์ ์ผ๋ก ์ถ์ถ
U ํ์
 ๊ฐ ๋ฉค๋ฒ์ ํจ์ ํ์
์ ์์ฑํ๊ณ , ๊ฐ ํจ์ ํ์
์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๋ก ํ์ฌ ์ธํฐ์น์
 ํ์
์ ์์ฑํ๋ ์์. โผ
type UnionToIntersection<U> = (
  U extends any ? (arg: U) => void : never // ์ฒซ๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
) extends (arg: infer R) => void // ๋๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
  ? R
  : never;
type T6 = { a: 'a' } | { b: 'b' };
type T7 = UnionToIntersection<T6>; // { a: 'a' } & { b: 'b' }
- ์ฒซ๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
 : 
U extends any ? (arg: U) => void : never- ํ์
 ๋ณ์ 
U๊ฐ ์ด๋ค ํ์ ์ด๋ ์๊ด์์ด(arg: U) => voidํ์ ์ผ๋ก ํ์ฅ - ํ์
 ๋ณ์ 
U์ ๊ฐ ๋ฉค๋ฒ์ ๋ํด(arg: U) => voidํ์ ์ ๊ฐ์ง ์ ๋์จ ๋ฐํ(arg: { a: "a" }) => void | (arg: { b: "b" }) => void 
 - ํ์
 ๋ณ์ 
 - ๋๋ฒ์งธ ์กฐ๊ฑด๋ถ ํ์
 : 
(...) extends (arg: infer R) => void ? R : never- ํจ์ ํ๋ผ๋ฏธํฐ ํ์
 
R์ถ๋ก - ๋ฐ๊ณต๋ณ ์์น์์ ํ์
 ๋ณ์(
R)์ ํ๋ณด๊ฐ ์ฌ๋ฌ๊ฐ์ด๋ฏ๋ก ์ธํฐ์น์  ํ์ ๋ฐํ 
 - ํจ์ ํ๋ผ๋ฏธํฐ ํ์
 
 
๋ ํผ๋ฐ์ค
Using TypeScript infer Like a Pro
Explained with animations. Master TypeScript Conditional Types and infer type inference.
levelup.gitconnected.com
๊ธ ์์ ์ฌํญ์ ๋ ธ์  ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Express] req.query vs req.params (0) | 2024.05.19 | 
|---|---|
| [JS] ํจ์ํ / ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๋ฎ์ ๊ผด (0) | 2024.05.19 | 
| [JS] JavaScript ์๋ฐ์คํฌ๋ฆฝํธ Map ๋ ์ ์ฌ์ฉํ๊ธฐ (0) | 2024.05.19 | 
| [CS] ๋๊ธฐ / ๋น๋๊ธฐ, ๋ธ๋กํน / ๋ ผ๋ธ๋กํน (0) | 2024.05.18 | 
| [JS] ์ ๊ท์์ ๊ทธ๋ฃนํ(Grouping) / ์บก์ฒํ(Capturing) ํ์ฉํ๊ธฐ (0) | 2024.05.18 | 
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
- 
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
 - 
์นด์นด์คํก
์นด์นด์คํก
 - 
๋ผ์ธ
๋ผ์ธ
 - 
ํธ์ํฐ
ํธ์ํฐ
 - 
Facebook
Facebook
 - 
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
 - 
๋ฐด๋
๋ฐด๋
 - 
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
 - 
Pocket
Pocket
 - 
Evernote
Evernote
 
๋ค๋ฅธ ๊ธ
- 
[Express] req.query vs req.params
[Express] req.query vs req.params
2024.05.19 - 
[JS] ํจ์ํ / ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๋ฎ์ ๊ผด
[JS] ํจ์ํ / ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๋ฎ์ ๊ผด
2024.05.19 - 
[JS] JavaScript ์๋ฐ์คํฌ๋ฆฝํธ Map ๋ ์ ์ฌ์ฉํ๊ธฐ
[JS] JavaScript ์๋ฐ์คํฌ๋ฆฝํธ Map ๋ ์ ์ฌ์ฉํ๊ธฐ
2024.05.19 - 
[CS] ๋๊ธฐ / ๋น๋๊ธฐ, ๋ธ๋กํน / ๋ ผ๋ธ๋กํน
[CS] ๋๊ธฐ / ๋น๋๊ธฐ, ๋ธ๋กํน / ๋ ผ๋ธ๋กํน
2024.05.18