Tip and Error

TypeScript에서 Type을 어떻게 쉽게 파악할까?

geonwoopaeng@gmail.com 2023. 1. 29. 19:12

TypeScript에서 Type을 어떻게 쉽게 파악할까?

TypeScript를 통해 프로젝트를 진행했었지만 단순히 VSCode에서 TypeScript의 type을 알려주는 기능을 통해 끼워 맞추고 있었습니다. 그래서 다른 분들의 코드를 이해하기에도 어려움이 따랐고 추 후 큰 프로젝트를 파악하기 위해서는 자세히 공부를 할 필요가 있다고 생각하게 되었습니다.

TypeScript를 다시 공부하면서 기본적인 TypeScript의 문법보다는 TypeScript를 다시 재정비하면서 TypeScript의 이 부분을 알게 되어 저한테 매우 도움이 되었다 및 개인 노하우? 를 토대로 작성 해보려고 합니다. 너무 깊게 들어가지 않으니 더 찾아보면 좋습니다. :)

(개인마다 차이가 있으니 재미로 읽어주시고 좋은 방법이 있으면 나눠주세요!!!)

👣 설명

1. 타입을 지원하는가(npm, yarn)

우선 가장 초기에 파악을 해야 할 점은 Type을 지원하냐 안하냐 입니다.

여러 라이브러리나 프레임워크들을 npm, yarn을 통해서 다운을 받습니다.
그래서 npm, yarn등 해당 사이트에 가서 사용하길 원하는 기술을 입력하여 Type을 지원하는지 알아야 합니다.

(저는 npm을 많이 사용하기 때문에 npm을 기준으로 설명하겠습니다.)
사용하기 원하는 라이브러리를 쳐서 들어가보면 옆에 다음과 같은 이모지들(DT, TS...)이 있습니다.

  • 없는 경우

Type자체가 없는 경우니 스스로 만들어서 사용하면 됩니다.

Screen Shot 2023-01-26 at 8 54 11 PM

  • DT

다른 사람이 만들어 놓은 Type(@types/react)이 있다는 의미로 DT를 눌러 이동하여 설치하면 됩니다. (*버전 확인!)

Screen Shot 2023-01-26 at 8 51 28 PM

  • TS

Type자체를 같이 가지고 있으니 추가로 설치하지 않아도 됩니다.

Screen Shot 2023-01-26 at 8 51 10 PM

추가로
해당 Type들을 잘 파악하고 싶은 경우 해당 Type들의 Github에 들어가서 package.json에 들어갑니다.
그래서 Type들을 대표하는 파일이 무엇인지 파악을 해야 하는데 보통 다음과 같이 types:로 나와 있는 파일이 대표 파일입니다.

Screen Shot 2023-01-26 at 9 09 07 PM

예로 든 redux의 대표 Type 파일은 index.d.ts이니 해당 파일을 보고 주석들과 module 시스템을 파악하면 추 후 사용할 때 도움이 많이 됩니다.

2. 단순히 타입으로만 보자

나같은 경우 초기에 변수 값과 Type이 같이 있어 변수 값을 같이 생각해서 더 복잡해졌었다.

예를 들어 [1,2,3,4,5]이 있으면 number[]만 생각해야 되는 데 [1,2,3,4,5]를 같이 생각했었습니다.
그래서 Type만 볼 때는 변수 값을 살짝 지우고 해보면 좋다.

3. 제너릭 함수 / this / overloading

다음은 .bind의 Type을 예시로 여러가지 상황에 대해 설명을 하겠습니다.

    bind<T>(this: T, thisArg: ThisParameterType<T>): OmitThisParameter<T>;

    bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;

    bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;

    bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;

    bind<T, A0, A1, A2, A3, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): (...args: A) => R;

    bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;

- 제너릭 함수

사용되는 제너릭이 많을 수록 많이 헷갈립니다.
그래서 무조건 순서대로 제너릭을 파악하는 것보다는 양방향으로 파악을 하면 이해하기 더 쉬워집니다. (특히 this의 제너릭 부분)
또한 제너릭을 빼고 이해를 해보면 더 쉽게 이해가 되는 경우도 많습니다.

- this

그냥 지우고 생각 하는 것이 정말 편합니다.
Type의 this는 매개변수 첫번째에 사용하도록 약속이 되어 있어 크게 신경쓰지 않아도 됩니다. :)

- overloading

다음과 같이 매개 변수에 따라 여러개의 type을 만들어 놓은 것을 볼 수 있습니다.
하지만 자신이 사용하고 있는 함수를 누르면 그냥 딱 맞는 Type에 데려다주니 크게 어려워할 필요는 없습니다.

6. 여러가지 사용 (declare, class, -, 블랜딩, unknown ...)

- declare

import에서 interface를 가지고 와서 같은 이름으로 확장하고 싶을 때 충돌이 나는 경우가 많습니다.
그때는 원래 선언되어 있던 것과 유사하게 선언하면 됩니다.

ex) Express 내부 inter()에 Status를 추가하려고 합니다.

// 본래
declare global {
    namespace Express {
      inter() {};
  }
}
// 확장
import { inter } from 'express'

Declare global {
    namespace Express {
        interface inter {
      Status: number;
    }
    }
}

interface inter {}

추가로 declare 부분이 까다롭기 때문에 공식 문서를 읽는 것이 매우 좋습니다. !!!
공식 문서: https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html

- class

TypeScript에서는 클래스는 Type자체로 많이 사용이 되고 d.ts파일, declare 등과 같이 type check 후에 없어지지 않으니 type 가드에 용이 합니다.
공식 문서: https://www.typescriptlang.org/docs/handbook/2/classes.html

- 마이너스(-)

TypeScript는 -를 사용할 수 없어 flat()같은 경우 숫자 -(마이너스) 한 것을 배열의 index를 통해 합니다.

type FlatArray<Arr, Depth extends number> = {
  done: Arr;
  recur: Arr extends ReadonlyArray<infer InnerArr>
    ? FlatArray<
        InnerArr,
        [ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ][Depth]
      >
    : Arr;
}[Depth extends -1 ? 'done' : 'recur'];

- 블랜딩

원시값을 구분하기 위한 방법입니다.

  type Brand<K, T> = K & { __ brand: T};

  type EUR = Brand<number, 'EUR'>
  type KRW = Brand<number, 'KRW'>

- unknown

어떤 값을 넣어야 할 지 모를때 사용하는 Type
any와 다르게 추 후 꼭 as등을 이용해서 Type을 지정해 줘야 한다.
(any의 경우 Type check를 바로 멈춰버린 점에서도 다르다.)

👣 마무리

TypeScript를 다시 공부를 하면서 Type을 새로 만들어 보고 여러 라이브러리들의 Type을 보고 숫자 마이너스가 안되서 배열을 통해 마이너스 역할을 하고 매개변수마다 Type을 만들어주는 것을 보고 TypeScript가 그렇게 잘 만들어진 언어는 아니라고 느껴졌습니다.
그리고 import한 함수의 Type을 새롭게 확장시키는 방식등 생각보다 복잡한 언어이기도 하다라고 느꼈습니다.

JavaScript의 자율성을 억제하기 위해 꼭 필요한 TypeScript를 다시 공부를 하면서 여러 Tip들을 얻었지만 아직 많이 부족하다는 생각을 가지고 있습니다. 결국, TypeScript를 많이 사용해야 겠습니다.

마지막 부분(여러가지 사용 파트)에 TypeScript를 하면서 알면 좋은 부분을 조금 추가해서 더러워 져서 죄송합니다.
글을 읽고 좋은 방법들이 있으신 분은 남겨주세요 !!!
긴 글 읽어주셔서 감사합니다. 좋은 하루 되세요 :)

👣 REF

https://gwpaeng.tistory.com/429
https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d
https://www.typescriptlang.org/docs/handbook/2/classes.html
https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html
https://www.npmjs.com/
https://yarnpkg.com/

반응형