๋ฐ˜์‘ํ˜•

TL;DR


  ์—„๊ฒฉ ๋ชจ๋“œ ๋น„์—„๊ฒฉ ๋ชจ๋“œ
ํ•จ์ˆ˜ ๋ฆฌํ„ด ํƒ€์ž… ๊ณต๋ณ€์  ๊ณต๋ณ€์ 
ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ๋ฐ˜๊ณต๋ณ€์  ์ด๋ณ€์ 
๊ทธ ์™ธ ๊ณต๋ณ€์  ๊ณต๋ณ€์ 
  • ๊ณต๋ณ€์„ฑ : ์Šˆํผ ํƒ€์ž…(๋„“์€ ํƒ€์ž…)์ด ์„œ๋ธŒ ํƒ€์ž…(์ข์€ ํƒ€์ž…)์„ ์ปค๋ฒ„ํ•˜๋Š” ๊ด€๊ณ„
    • ์„œ๋ธŒ ํƒ€์ž…์„ ์Šˆํผ ํƒ€์ž…์— ๋Œ€์ž…ํ•  ์ˆ˜ ์žˆ์Œ e.g. superType = subType
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ณธ ๋™์ž‘
  • ๋ฐ˜๊ณต๋ณ€์„ฑ : ์„œ๋ธŒ ํƒ€์ž…(์ข์€ ํƒ€์ž…)์ด ์Šˆํผ ํƒ€์ž…(๋„“์€ ํƒ€์ž…)์„ ์ปค๋ฒ„ํ•˜๋Š” ๊ด€๊ณ„
    • ์Šˆํผ ํƒ€์ž…์„ ์„œ๋ธŒ ํƒ€์ž…์— ๋Œ€์ž…ํ•  ์ˆ˜ ์žˆ์Œ e.g. subType = superType
    • ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋™์ž‘
  • ์ด๋ณ€์„ฑ : ๊ณต๋ณ€์„ฑ๊ณผ ๋ฐ˜๊ณต๋ณ€์„ฑ์„ ๋ชจ๋‘ ํฌํ•จํ•˜๋Š” ๊ด€๊ณ„
    • ์Šˆํผ ํƒ€์ž…๊ณผ ์„œ๋ธŒ ํƒ€์ž…์„ ๋ชจ๋‘ ๋Œ€์ž…ํ•  ์ˆ˜ ์žˆ์Œ
    • strictFunctionTypes ์˜ต์…˜์„ ๊ป์„ ๋•Œ ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋™์ž‘

 

๊ณต๋ณ€์„ฑ | Covariance


๊ณต๋ณ€์„ฑ : A๊ฐ€ B์˜ ์„œ๋ธŒ ํƒ€์ž…์ด๋ฉด, T<A>๋Š” T<B>์˜ ์„œ๋ธŒ ํƒ€์ž…

 

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ superArray(string | number)๋Š” subArray(string)๋ณด๋‹ค ๋„“์€ ํƒ€์ž…์„ ๊ฐ€์ง„๋‹ค. ๋”ฐ๋ผ์„œ string์€ string | number์— ํฌํ•จ๋˜๋Š” ์„œ๋ธŒ ํƒ€์ž…์ด๊ณ , string | number๋Š” string์„ ํฌํ•จํ•˜๋Š” ์Šˆํผ ํƒ€์ž…์ด๋‹ค.

 

์ข์€ ํƒ€์ž…์ธ subArray๋ฅผ ๋„“์€ ํƒ€์ž…์ธ superArray์— ๋Œ€์ž…ํ•˜๋Š”๊ฑด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ˜๋ฉด ๋„“์€ ํƒ€์ž…์„ ์ข์€ ํƒ€์ž…์— ๋Œ€์ž…ํ•  ๋• ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

// ๊ณต๋ณ€์„ฑ ์˜ˆ์‹œ
let superArray: Array<string | number> = [];
let subArray: Array<string> = [];

superArray = subArray; // OK
subArray = superArray; // Error 'number' ํ˜•์‹์€ 'string' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค(ts2322)

 

์ด์ฒ˜๋Ÿผ ์ข์€ ํƒ€์ž…์„ ๋„“์€ ํƒ€์ž…์— ๋Œ€์ž… ํ•  ์ˆ˜ ์žˆ๋Š” ๋™์ž‘์„ ๊ณต๋ณ€์„ฑ(Covariance; ๅๆ–นๅทฎ)์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณต๋ณ€์„ฑ์„ ๊ฐ–๋Š”๋‹ค.

 

ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด ํƒ€์ž… ์—ญ์‹œ ๊ณต๋ณ€์„ฑ์„ ๊ฐ€์ง„๋‹ค. a์˜ ๋ฆฌํ„ด ํƒ€์ž…(number)์ด B์˜ ๋ฆฌํ„ด ํƒ€์ž…(number | string)๋ณด๋‹ค ์ข์€ ํƒ€์ž…์ด๋ฏ€๋กœ, a๋ฅผ b์— ๋Œ€์ž…ํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

// ํ•จ์ˆ˜ ๋ฆฌํ„ด ํƒ€์ž…์˜ ๊ณต๋ณ€์„ฑ — 1
function a(x: string): number { return 0; }
type B = (x: string) => number | string;

const b: B = a; // OK (a ๋ฆฌํ„ด ํƒ€์ž… < B ๋ฆฌํ„ด ํƒ€์ž…)

 

๋ฐ˜๋Œ€๋กœ a ๋ฆฌํ„ด ํƒ€์ž…์ด ๋” ๋„“์€ ํƒ€์ž…์„ ๊ฐ–๋„๋ก ๋ณ€๊ฒฝํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

// ํ•จ์ˆ˜ ๋ฆฌํ„ด ํƒ€์ž…์˜ ๊ณต๋ณ€์„ฑ — 2
function a(x: string): number | string { return 0; }
type B = (x: string) => number;

const b: B = a; // Error (a ๋ฆฌํ„ด ํƒ€์ž… > B ๋ฆฌํ„ด ํƒ€์ž…)

 

๋ฐ˜๊ณต๋ณ€์„ฑ | Contravariance


๋ฐ˜๊ณต๋ณ€์„ฑ : A๊ฐ€ B์˜ ์„œ๋ธŒ ํƒ€์ž…์ด๋ฉด, T<B>๋Š” T<A>์˜ ์„œ๋ธŒ ํƒ€์ž…

 

๋„“์€ ํƒ€์ž…์„ ์ข์€ ํƒ€์ž…์— ๋Œ€์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๋™์ž‘์„ ๋ฐ˜๊ณต๋ณ€์„ฑ์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„  ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ฐ˜๊ณต๋ณ€์„ฑ์„ ๊ฐ–๋Š”๋‹ค. — strictFunctionTypes ์˜ต์…˜์„ ์ผฐ์„ ๋•Œ

 

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ ๋„“์€ ํƒ€์ž…(์Šˆํผ ํƒ€์ž…)์„ ๊ฐ€์ง„ log๋ฅผ ์ข์€ ํƒ€์ž…(์„œ๋ธŒ ํƒ€์ž…)์„ ๊ฐ€์ง„ logNumber์— ๋Œ€์ž…ํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. number(log) ํƒ€์ž…์€ string | number(logNumber) ํƒ€์ž…์˜ ์„œ๋ธŒ ํƒ€์ž…์ด์ง€๋งŒ, ์˜คํžˆ๋ ค Logger<string | number> ๊ฐ€ Logger<number>์˜ ์„œ๋ธŒ ํƒ€์ž…์œผ๋กœ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค. ์„œ๋ธŒ ํƒ€์ž…์ด ์Šˆํผ ํƒ€์ž…์„ ์ปค๋ฒ„ ํ•˜๋Š”์…ˆ์ด๋‹ค.

// ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๋ฐ˜๊ณต๋ณ€์„ฑ ์˜ˆ์‹œ1 - ์ฝ”๋“œ ์ฐธ๊ณ  via seob.dev
type Logger<T> = (param: T) => void;

let log: Logger<string | number> = (param) => { console.log(param); };
let logNumber: Logger<number> = (param) => { console.log(param); };

log = logNumber; // Error
logNumber = log; // OK

 

ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ฐ˜๊ณต๋ณ€์„ฑ์„ ๊ฐ–๋Š” ์ด์œ ๋Š” ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค. Adder ํƒ€์ž…์„ ๊ฐ–๋Š” ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” string, number ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

// ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๋ฐ˜๊ณต๋ณ€์„ฑ ์˜ˆ์‹œ2 - ์ฝ”๋“œ ์ฐธ๊ณ  via seob.dev
type Adder = (a: string | number, b: string | number) => string | number;
let add: Adder;

add = (a: number, b: number) => { return a + b; }; // Error

 

๋งŒ์•ฝ number ํƒ€์ž…๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ• ๋‹นํ•œ๋‹ค๋ฉด string ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์–ด์„œ Type-Safe ํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค. ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ฐ˜๊ณต๋ณ€์„ฑ์„ ๊ฐ–๋Š”๊ฑด ์–ด์ฐŒ๋ณด๋ฉด ๋‹น์—ฐํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

 

๐Ÿ’ก ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” strictFunctionTypes ์˜ต์…˜์„ ์ผฐ์„ ๋•(true) ๋ฐ˜๊ณต๋ณ€์„ฑ์„, ํ•ด๋‹น ์˜ต์…˜์„ ๊ป์„ ๋•(false) ์ด๋ณ€์„ฑ์„ ๊ฐ€์ง„๋‹ค. strictFunctionTypes ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’์€ true์ด๋‹ค.

 

์ด๋ณ€์„ฑ | Bivariance


์ด๋ณ€์„ฑ : A๊ฐ€ B์˜ ์„œ๋ธŒ ํƒ€์ž…์ผ ๋•Œ, T<A>๋Š” T<B>์˜ ์„œ๋ธŒ ํƒ€์ž…์ธ ๋™์‹œ์— ์Šˆํผ ํƒ€์ž…

 

--strictFunctionTypes ์˜ต์…˜์„ ๋„๋ฉด ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์ด๋ณ€์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. ์ด๋ณ€์„ฑ์€ ๊ณต๋ณ€์„ฑ๊ณผ ๋ฐ˜๊ณต๋ณ€์„ฑ์„ ๋ชจ๋‘ ๊ฐ€์ง€๋Š” ๊ฒƒ์„ ๋œปํ•œ๋‹ค. ์ฆ‰, ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์„œ๋ธŒ ํƒ€์ž…๊ณผ ์Šˆํผ ํƒ€์ž…์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋ž˜ ์˜ˆ์ œ์—์„  Logger<string | number | boolean>, Logger<number> ๋‘˜๋‹ค Logger<string | number> ์˜ ์„œ๋ธŒ ํƒ€์ž…์œผ๋กœ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค.

// ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ด๋ณ€์„ฑ ์˜ˆ์‹œ - ์ฝ”๋“œ ์ฐธ๊ณ  via seob.dev
type Logger<T> = (param: T) => void;
let log: Logger<string | number> = (param) => { console.log(param); };

log = (param: string | number | boolean) => { console.log(param); }; // OK
log = (param: number) => { console.log(param); }; // OK

 

๋ฉ”์„œ๋“œ ํƒ€์ž… ์ •์˜ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ ์ฐจ์ด์ 


๐Ÿ’ก Array ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ๊ณต๋ณ€์ ์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์•„๋ž˜ ๊ฐ™์€ ์ฐจ์ด์ ์„ ๋‘” ๊ฒƒ์ด๋ผ๊ณ  ํ•จ (์ฐธ๊ณ )

 

๋ฉ”์„œ๋“œ ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์€ ํ”„๋กœํผํ‹ฐ(property) ๋ฐฉ์‹๊ณผ ์ค„์—ฌ์“ฐ๊ธฐ(shorthand) ๋ฐฉ์‹ 2๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ์ด ๋‘˜์€ ์–ผํ•๋ณด๋ฉด ๊ฐ™์•„ ๋ณด์ด์ง€๋งŒ ๋ฏธ๋ฌ˜ํ•œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์€ ๋ฐ˜๊ณต๋ณ€์ ์œผ๋กœ ์ž‘๋™ํ•˜๊ณ , ์ค„์—ฌ์“ฐ๊ธฐ ๋ฐฉ์‹์€ ์ด๋ณ€์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค.

interface State<T> {
  set: (item: T) => void; // ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹(๋ฐ˜๊ณต๋ณ€์ )
}

interface State<T> {
  set(item: T): void; // ์ค„์—ฌ์“ฐ๊ธฐ ๋ฐฉ์‹(์ด๋ณ€์ )
}

 

strictFunctionTypes ์˜ต์…˜์ด ์ผœ์ ธ์žˆ๋‹ค๋ฉด ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ๋ฐ˜๊ณต๋ณ€์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„  ์„œ๋ธŒ ํƒ€์ž…(number)์„ ์Šˆํผ ํƒ€์ž…(number | string)์— ๋Œ€์ž…ํ•˜๊ณ  ์žˆ๋‹ค. set ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ string ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

interface State<T> {
  set: (item: T) => void; // ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹
}

const state: State<number | string> = {
  set(item: number) { /* ... */ }, // ๊ณต๋ณ€์„ฑ Error
};

 

๋ฉ”์„œ๋“œ ํƒ€์ž… ์ •์˜๋ฅผ ์ค„์—ฌ์“ฐ๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •ํ•˜๋ฉด ๊ณต๋ณ€์ ์œผ๋กœ๋„ ์ž‘๋™ํ•ด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค(์ด๋ณ€์ ์ด ๋จ).

interface State<T> {
  set(item: T): void; // ์ค„์—ฌ์“ฐ๊ธฐ ๋ฐฉ์‹
}

const state: State<number | string> = {
  set(item: number) { /* ... */ }, // ๊ณต๋ณ€์„ฑ OK
};

const state: State<number | string> = {
  set(item: number | string | boolean) { /* ... */ }, // ๋ฐ˜๊ณต๋ณ€์„ฑ OK
};

 

๋ฒˆ์™ธ | ํƒ€์ž… ํฌํ•จ ๊ด€๊ณ„ ํ‘œ๊ธฐ๋ฒ•


  • A <: B : A๋Š” B์˜ ์„œ๋ธŒ ํƒ€์ž…
  • A -> B : ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์€ A, ๋ฐ˜ํ™˜ ํƒ€์ž…์€ B

 

๋ ˆํผ๋Ÿฐ์Šค


 


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