TIL - TypeScript #3

wononly.dev·2022년 9월 7일

TIL

목록 보기
3/3
post-thumbnail

Call Signatures

함수의 인자(arguments) 타입과 반환 타입을 알려주는 것이다.

  • 선언 방법

    type Add = (a: number, b: number) => number
    // or 
     type Add = {
        (a: number, b: number): number
     }
    
     const add: Add = (a, b) => a + b b 

오버로딩(Overloading)

  • 오버로딩이란 이름은 같지만 파라미터 개수, 타입(시그니처)이 다른 함수를 중복으로 선언하는 것을 의미한다.
  • 함수가 서로 다른 여러 개의 call signature를 가질 때 오버로딩이 발생한다.
    즉, 오버로딩은 여러 call signature가 있는 함수를 말하는 것이다.

    type Add = {
    	(a: number, b: number): number
    	(a: number, b: string): number
    }
    
    const add: Add = (a, b) => {
    	if (typeof b === 'string') return a
    	return a + b 
    }
  • 여러 개의 argument를 가지고 있는 경우 (자주 사용하는 방법은 아님)

    • 다른 개수의 파라미터를 가지게 되면, 나머지 파라미터의 타입을 지정해줘야 한다.

      type Add = {
      	(a: number, b: number): number
          (a: number, b: string, c: number): number // c파라미터는 optional함
      }
      
      const add: Add = (a, b, c?: number) => { // c가 선택사항이라는 것을 알려줘야함
      	if (c) return a + b + c	
          return a + b
      }
      
      add(1, 2)
      add(1, 2, 3)

다형성(Polymorphism)

  • 다형성이란?
    • 'poly'는 그리스어로 많은, 다수의 란 뜻이다. 'morphos'는 형태, 구조 라는 뜻을 가지고있다.
      이 둘을 조합하면 'many structure'. 즉, 여러가지 구조, 형태 라고 해석할 수 있다.

concrete 타입

number, boolean, string, void, unknown 타입 등이 있다.

generic 타입

placeholder를 사용하여 작성한 코드의 타입 기준을 바꿔준다. concrete 타입을 사용하는 것 대신 사용할 수 있는 타입이다.

  • 타입스크립트가 해당 타입을 유추할 수 있다.

  • Call Signature 를 작성할 때, 확실한 타입을 모르는 경우 generic 타입을 사용한다.

  • < T > : placeholder 를 사용하여 타입스크립트에게 generic 타입을 받고 싶다는 것을 표현하는 식

  • generic 타입을 이용하면 인수의 타입을 모두 정의해 줄 필요가 없다.

    // generic 타입 적용 전
    type SuperPrint = {
        (arr: number | boolean | string[]): void
    }
    
    // generic 타입 적용 후
    type SuperPrint = {
        <TypePlaceholder>(arr: TypePlaceholder[]): void
    }
    
    const superPrint: SuperPrint = (arr): void => {
        arr.forEach(i => console.log(i))
    }
  • generic 타입을 사용한 Call Signagture 를 지정한 경우 타입스크립트가 추론한 타입으로 Call Signature가 대체된다.

    • 예제1

        type SuperPrint = {
            <TypePlaceholder>(arr: TypePlaceholder[]): void
        }
      
        superPrint([1, 2, 3, 4])
      
        // 타입스크립트가 추론하여 대체한 call signature
        const superPrint: <number> = (arr: number[]): void => {
            arr.forEach(i => console.log(i))
        }
    • 예제2

      type SuperPrint = {
          <TypePlaceholder>(arr: TypePlaceholder[]): void
      }
      
      superPrint([1, 2, true, 'a'])
      
      // 타입스크립트가 추론하여 대체한 call signature
      const superPrint: <number | boolean | string> = (arr: (number | boolean | string)[]): void => {
          arr.forEach(i => console.log(i))
      }
  • 리턴 타입을 바꾸고 싶은 경우

    type SuperPrint = <T>(arr: T[]) => T
    
    const superPrint: SuperPrint = (arr) => arr[0]
    
    const a = superPrint([1, 2, 3, 4])
    const b = superPrint([true, true, false, false])
    const c = superPrint(['a', 'b', 'c', 'd'])
    const d = superPrint([1, 2, true, 'a'])

Generics 재정리♻️

타입스크립트는 작성한 코드를 보고 타입을 유추하여 placeholer로 정의한 generic 타입을 대체한다.

any와 generic 타입의 차이는???

  • any 와 generic 은 동일한 작업을 하지 않는다.

  • generic 은 우리가 하는 요청에 따라 call signature를 생성한다.

  • any는 어떠한 타입도 정의될 수 있다는 뜻이여서 타입스크립트가 보호해주지 않는다.

    type SuperPrint = (arr: any[]) => any
    
    const superPrint: SuperPrint = (arr) => arr[0]
    
    const a = superPrint([1, 2, 3, 4])
    const b = superPrint([true, true, false, false])
    const c = superPrint(['a', 'b', 'c', 'd'])
    const d = superPrint([1, 2, true, 'a'])
    
    d.toUpperCase() // 라인 에러 발생 X
  • generic 은 타입스크립트에서 타입을 유추하여 대체하기 때문에 타입스크립트가 보호해 줄 수 있다.

    type SuperPrint = <T>(arr: T[]) => T
    
    const superPrint: SuperPrint = (arr) => arr[0]
    
    const a = superPrint([1, 2, 3, 4])
    const b = superPrint([true, true, false, false])
    const c = superPrint(['a', 'b', 'c', 'd'])
    const d = superPrint([1, 2, true, 'a'])
    
    d.toUpperCase() // 라인 에러 발생
  • generic 은 사용자가 요구한대로 signature 를 생성해줄 수 있는 도구

  • 여러 개의 generic 타입을 사용하고 싶은 경우

    • 타입스크립트는 generic 을 처음으로 인식했을 때와 generic 의 순서를 기반으로 generic 의 타입을 유추하게 됨

      type SuperPrint = <T, V>(a: T[], b: V) => T
      
      const superPrint: SuperPrint = (arr) => arr[0]
      
      const a = superPrint([1, 2, 3, 4], 'x')
      const b = superPrint([true, true, false, false])
      const c = superPrint(['a', 'b', 'c', 'd'])
      const d = superPrint([1, 2, true, 'a'])

결론

  • generic 타입은 실제 개발하는 경우 잘 사용하지 않는 타입이다.
    다만 라이브러리를 만들거나, 다른 개발자가 사용할 기능을 개발하는 경우엔 유용하다.

  • Call signature 외에 도 다른 방법으로 사용가능하다.

    • generic을 사용하여 타입을 생성할수도, 어떤 경우는 타입을 재사용하여 타입을 확장할 수 있다.
  • 대부분의 기본적인 타입스크립트의 타입은 generic 으로 만들어져있다.

    const numArr: number[] = [1, 2, 3, 4]
    
    const numArr: Array<number> = [1, 2, 3, 4]
profile
항상 이유와 과정을 궁금해하는🤔 백엔드 개발자의 기술 블로그 입니다!

0개의 댓글