[Typescript] 다형성과 제네릭

sunn·2022년 7월 20일
0
post-thumbnail

Polymorphism

다형성. 말 그대로 Typescript의 인자나 값들은 다양한 Type을 가질 수 있다.

Generic

제네릭은 C#이나 Java와 같은 언어에서 재사용 가능한 컴포넌트를 만들기 위해 사용하는 기법입니다. 단일 타입이 아닌 다양한 타입에서 작동할 수 있는 컴포넌트를 생성할 수 있습니다.
(구체적인 타입을 지정하지 않고 다양한 인수와 리턴 값에 대한 타입을 처리할 수 있다.)
타입스크립트에서 제네릭을 통해 인터페이스, 함수 등의 재사용성을 높일 수 있습니다.

type SuperPrint = <T>(a: T[]) => T
  // -> T는 배열에서 오고, 함수의 첫번째 파라미터에서 온다.
  // <꺽쇠>를 사용해 TS가 유추한 타입으로 대체할 수 있다.
  // 많은 타입을 동시에 가지게 할 수 있음!

const superPrint: SuperPrint = (a) => a[0]

const a = superPrint([1, 2, 3, 4]) // a: number
const b = superPrint([true, false, true, false]) // b: boolean
const c = superPrint(['a', 'b', 'c']) // c: string
const d = superPrint([1, 2, true, false]) // d: number | boolean
// 함수마다 다른 타입의 값이 들어가고 있음에도 오류가 없다!

여기서 사용한 <T>제네릭<Generic>이라고 하고, 제네릭을 사용하면 타입스트립트가 이 함수의 call signature를 만들어주기 때문에 보호받을 수 있다.

제네릭은 사용자가 요구한 대로 call signature를 생성해줄 수 있는 도구이다.

  • any를 붙여도 같은 의미가 아닐까? 하고 생각할 수도 있지만
type SuperPrint = {
	(arr: any[]): any
}

const superPrint: SuperPrint = (arr) => arr[0]

let a = superPrint([1, "b", true]);
// pass
a.toUpperCase();

any를 사용하게 되면 제네릭처럼 보호받을 수 없기 때문에(call signature: any로 지정) 사용을 지양하자!

복수의 제네릭을 사용한 예

type SuperPrint = <T,M>(a: T[], b: M) => T
// -> M이라는 제네릭이 추가됐을 때

const superPrint: SuperPrint = (a) => a[0]

const a = superPrint([1, 2, 3, 4], "X") // (a: number[], b: string) => number
const b = superPrint([true, false, true, false], 1) // (a: boolean[], b: number) => boolean
const c = superPrint(['a', 'b', 'c'], false) // (a: string[], b: boolean) => string
const d = superPrint([1, 2, true, false], "TS") // (a: (number | boolean)[], b: string) => number | boolean

타입스크립트는 제네릭을 처음 인식했을 때와 제네릭의 순서를 기반으로 제네릭의 타입을 알게 된다. 따라서 위와 같이 복수의 제네릭을 사용할 수도 있다!

제네릭의 간단한 실 사용 예제

  • 타입을 중첩해 적용할 경우
type Player<E> = {
  name: string
  extraInfo: E
} // 제네릭<E>을 가진 Player 객체의 형태, extraInfo에 제네릭이 들어가고 있다.

type NicoExtra = {
  favFood:string
} // Player.extraInfo에 들어갈 타입

type NicoPlayer = Player<NicoExtra>
// Player<E>와 같은 형태

const nico: NicoPlayer = {
  name:"nico",
  extraInfo: {
    favFood: "kimchi"
  }
} // nico에 NicoPlayer 타입을 지정해 한번에 모든 타입 지정이 완료된 모습
  • React의 useState에서의 활용
useState() // TS는 state의 타입을 알 수 없음
->
useState<number>() // useState가 number 타입을 가질 경우
profile
:-)

0개의 댓글