Into The Typescript Project

wook2·2021년 3월 23일
0

typescript

목록 보기
4/4
post-thumbnail

본 포스트는 인프런 타입스크립트 강의를 바탕으로 작성되었습니다. https://www.inflearn.com/course/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%EC%A0%84/dashboard

기존 JS프로젝트를 TS로 바꾸기

프로젝트 설정

JS파일을 TS파일로 바꾸면 TS문법을 적용하여 타입에러가 나오는 것을 볼 수 있다.

모든 JS파일을 바꾸기보다는 TS가 필요한 파일에 먼저 적용하여 점진적으로 바꾸어 나가는 것이 올바른 방식이다.

  • tsconfig.json
    타입스크립트 설정이다. include의 와일드카드 패턴의 [**] 은 하위 디렉터리를 재귀적으로 접근한다는 뜻이고, [*] 은 해당 디렉터리의 모든 파일을 의미한다.
{
    "compilerOptions": {
        "allowJs": true, // 이 파일안에 있는 JS파일도 허용한다는 옵션
        "target" : "ES5",
        "outDir": "./built", // 타입스크립트 컴파일 결과물의 저장 위치 지정
        "moduleResolution": "Node", // 모듈해석전략에 따라 classic 과 Node로 나뉜다.
    },
    "include": ["./src/**/*"] 
  // 프로젝트를 기준으로 어떤 파일을 컴파일에 포함시킬것인가에 대한 옵션, /**/* src아래의 모든 파일을 포함시키겠다
}

자세한 옵션설정은 https://www.typescriptlang.org/docs/handbook/tsconfig-json.html 를 참고

  • package.json
    npm init -y 명령어로 package.json 파일을 생성했다. 스크립트에 build를 추가해 타입스크립트를 컴파일 하는 명령어를 추가하였다.
{
  "name": "project",
  "version": "1.0.0",
  "description": "https://www.inflearn.com/course/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%EC%A0%84/dashboard",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^4.2.3"
  }
}
  • 빌드해보기
    아래의 사진과 같이 타입에러를 알려주는 것을 확인할 수 있다.

점진적인 타입 적용

1. 명시적인 any 선언하기
tsconfig파일에 noImplicitAny 옵션을 주어 명시적인 any를 사용하도록 하였다.

"noImplicitAny": true

2. API 타입 선언하기
API 레퍼런스를 참고해 매개변수의 타입을 적용한다.

function fetchCountryInfo(countryCode, status) {
 // params: confirmed, recovered, deaths
 const url = `https://api.covid19api.com/country/${countryCode}/status/${status}`;
 return axios.get(url);
}

status에는 confirmed, recovered, deaths 가 있는것을 확인할 수 있다.

인자로 3개만 들어갈 수 있으니, enum을 이용해 타입을 적용한다.

enum CovidStatus {
  Confirmed = 'confirmed',
  Recovered = 'recovered',
  Deaths = 'deaths',
}

3. Element 타입

Element 타입에 innerText속성이 없다고 나온다. 즉 Element 타입을 좀더 자세히 명시해 주어야한다.

Element타입의 정의부분을 보면 Element는 Document로 받아올 수 있는 모든 객체를 포함한다고 되어있다.

Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element.

Element는 아래와 같이 여러 타입을 상속받아와 정의되어있다.

Element를 따라가 타입을 확인한 뒤, 구체적으로 타입 명시를 위해 타입 단언을 하였다.

const confirmedTotal = $('.confirmed-total') as HTMLSpanElement;
const deathsTotal = $('.deaths') as HTMLParagraphElement;
const recoveredTotal = $('.recovered') as HTMLParagraphElement;
const lastUpdatedTime = $('.last-updated-time') as HTMLLIElement;

프로젝트 환경 구성

eslint

eslint는 자바스크립트 문법검사를 해주는데, 문법검사에 다양한 옵션을 추가할 수 있다.

- eslint --init
eslint --init 명령어를 통해 cli로 .eslint.js 파일을 생성할 수 있다.

eslint 를 설치하고 나면 아래와 같이 eslint가 문법검사를 한 결과를 보여준다.

VScode에서 setting.json 파일에 아래와 같은 옵션을 주면, 저장시 잘못된 부분이 있다면 eslint가 자동으로 코드 수정을 한다.

Babel

브라우저 호환을 위해 Babel을 사용한다.

  • @babel/preset-env : 브라우저에 맞게 문법을 변환하여 주는 라이브러리이다. 함께 사용되어야 하는 Babel 플러그인을 모아 둔 것으로 Babel 프리셋이라고 부른다
  • "@babel/core": 바벨 core 라이브러리
  • "@babel/preset-typescript": 타입스크립트 변환을 위한 라이브러리

Prettier

Prettier는 정해진 규칙으로 코드 스타일을 정리해주는 도구이다. 팀단위 개발을 할때는 팀단위 코딩 컨벤션을 설정하고 개발해야 개발자마다 코딩 스타일이 다른것을 잡아줄 수 있다.

.eslint.js 에서 prettier 옵션을 설정할 수 있다.

rules: {
    "prettier/prettier": [
      "error",
      {
        endOfLine: "auto",
      },
      {
        singleQuote: true,
        semi: true,
        useTabs: false,
        tabWidth: 2,
        printWidth: 80,
        bracketSpacing: true,
        arrowParens: "avoid",
      },
    ],
  },

tslint vs eslint

아래의 링크에 해당 issue에 대해서 나와있다.

https://github.com/microsoft/TypeScript/issues/29288

현재 tslint는 구조적으로 문제가 있어서 처음부터 뜯어 고쳐야하는데, 그러면 기존 tslint를 쓰는 프로그램은 작동이 안되는 문제가 있다고 한다.
그렇기 때문에 현재 eslint에 좀더 비중을 두고 있고, eslint위에 ts를 얹어서 사용을 권장하고 있다.


외부 라이브러리 모듈화

기존 cdn 방식으로 불러왔던 라이브러리를 타입스크립트가 인식할 수 있게 npm으로 라이브러리를 다운로드 하여 모듈화를 한다.

아래와 같이 불러올 수 있다.

filename.d.ts

.d.ts파일은 기존의 JS 라이브러리에서 TS가 발전하면서 생기게 되었다. .d.ts파일에 라이브러리의 타입을 정의해놓은 라이브러리가 있지만 그렇지 않은 라이브러리도 있다.

'npm i --save-dev @types/chart.js'을(를) 시도하거나, 'declare module 'chart.js';'을(를) 포함하는 새 선언(.d.ts) 파일 추가

Definitely Typed

라이브러리들의 타입정의를 @types에 모두 모여있다.

https://www.typescriptlang.org/dt/search?search=

위의 사이트에 들어가면 사용하는 라이브러리가 타입정의가 지원되는지 확인할 수 있다.

  • 타입스크립트 옵션으로 typeRoots속성을 넣어주면, 속성의 경로들을 참조하여 타입을 사용한다.

api 타입 정의

axios라이브러리의 axios 타입을 다음과 같이 AxiosResponse로 알려주는 것을 확인할 수 있다. 이제 AxiosResponse로 어떤 타입이 들어오는지 정의를 해주면 된다.


개발자 도구에 들어가 받아올 객체가 어떤 모양인지 확인한다.


응답 객체의 모양에 따라 인터페이스를 생성하였다.

interface CovidSummaryResponse {
  Countries: any[];
  Date: string;
  Global: any;
  Message: string;
}

AxiosResponse에 만든 응답 타입을 넣고 then()으로 함수를 받아오면 아래와 같이 자동으로 어떤 프로퍼티가 있는지 알려준다.


strict 옵션 적용

strict는 말그대로 엄격한 타입체킹을 사용한다는 뜻이다.

Tsconfig reference에 다음과 같은 옵션들이 있다.

기본 strict옵션만 적용하면 나머지 옵션들의 default값은 true로 적용된다.

예시1)

  • strictFuctionTypes
    addEventListner의 스펙을 보면 콜백함수에 Event 객체를 전달한다고 나와있다.

하지만 콜백함수에 인자로 MouseEvent 객체를 전달하면 strictFuctionTypes 옵션에 걸리게 된다. 즉 해당 옵션은 정확한 스펙을 맞추어 주어야 하는 옵션이다.

async function handleListClick(**event: MouseEvent**) {
...
}

null 값 해결하기

1. if문으로 null값 처리
가장 기본적인 방식으로 null값을 if문으로 감싸주어 null값은 가능하지 않게 하였다.

function initEvents() {
  if (!rankList) {
    return;
  }
  rankList.addEventListener("click", handleListClick);
}

2. 타입 단언을 이용한 해결
다음과 같이 ! (assert null operator)를 사용해 해결할 수 있다. !를 통해 이 변수는 null 이나 undefined 값이 올 수 없음을 타입 단언 하는 방식이다.

deathsList!.appendChild(li);

하지만 이것은 어디나 임시방편의 해결책으로, 아래와 같이 eslint에서 non-null assertion에 대해 경고하는 메시지를 보낸다.

3. 옵셔널 체이닝을 이용한 해결
optional chaining은 비교적 최근에 나온 JS문법이다. 체이닝을 접근하는 과정에서 객체가 nullish하다면 에러를 발생시키지 않고 undefined를 반환한다.
1번방법을 한 문자로 줄인것이라 생각하면 된다.
아래의 두가지 방법이 같은 의미이다.

if(!recoveredList){
      return
    } else{
      recoveredList.appendChild(li);
    }
recoveredList?.appendChild(li);
profile
꾸준히 공부하자

0개의 댓글