Front-end/TypeScript

[TypeScript] 엄격한 타입 strict 옵션 적용

이안92 2021. 6. 23. 16:03
반응형

 strict 옵션이란?

 

정의해놓은 코드에서 강하게 타입을 정의할 수 있고 추후에 일어날 수 있는 타입 정의에 대한 오류 대응할 수 있고 안전하게 코딩을 할 수있게 한다.

 

tsconfig.json에서 strick 옵션은 default가 false이며 true로 고쳐주면 아래의 옵션들도 같이 true가 된다.

{
  "strict": true,
  "strictNullChecks": true,
  "strictFunctionTypes": true,
  "strictBindCallApply": true,
  "strictPropertyInitialization": true,
  "noImplicitThis": true,
  "alwaysStrict": true,
}

 

내장 타입의 위계 구조 타입 오류

 

// 타입스크립트 내장 타입의 위계 구조 아래로 갈수록 하위
// const a: Element
// const b: HTMLElement
// const c: HTMLDivElement

// const evt1: Event
// const evt2: UIEvent
// const evt3: MouseEvent

async function handleListClick(event: Event) {
// MouseEvent는 UIEvent를 상속, UIEvent는 Event를 상속

 

null 타입 오류 / Object is possibly 'null'.ts(2531) (null일 수도 있다)
if (
    event.target instanceof HTMLParagraphElement ||
    event.target instanceof HTMLSpanElement
  ) {
    selectedId = event.target.parentElement.id;
  }

if (
    event.target instanceof HTMLParagraphElement ||
    event.target instanceof HTMLSpanElement
  ) {
    if (!event.target.parentElemnt) {
      return;
    }
    selectedId = event.target.parentElement.id
  }

위와 같이 존재하지 않으면 return으로 종료하여 해결할 수도 있고 아래와 같이 삼항연산자를 이용해도 된다.

if (
    event.target instanceof HTMLParagraphElement ||
    event.target instanceof HTMLSpanElement
  ) {
    selectedId = event.target.parentElement // 있으면 id 찾기
      ? event.target.parentElement.id
      : undefined; // null 타입 오류 해결
  }

 

Type 'undefined' is not assignable to type 'string'. ts(2345)

 

fetchCountryInfo에 countryName을 string으로 받을 수있게 했기 때문에 아래와 같이 오류가 생긴다.

string에 undefined까지 추가해준다.

 

타입단언

 

querySelector는 Element 또는 null이 단언 타입이다.

! type assertion로 null이 아니라고 단언을 하면 eslint에서 Forbidden non-null assertion이라는 에러가 뜬다.

interface Person {
  name: string;
  skill: string;
}

const ian: Person = {
  name: 'ianlee',
  skill: 'react',
};

// type annotation ":"
// 정의만하고 속성, 객체 정의하지 않으면 타입오류가 남(장점)
const ian: Person = {};

위와 같이 Person이라고 인터페이스를 주었을 때 name, skill 스펙을 가지고 타입 정의를 할 수 있고,

정의만하고 name, skill을 정의하지 않으면 오류가 나서 타입스크립트의 장점을 얻을 수 있다.

// as 타입단언(assertion) 객체에 Person라고 하는 인터페이스를 둘거다
// 타입 오류가 나지 않으므로 초기값을 정하지 않은 누락 등 에러 발생 가능
const ian = {} as Person;
// ian.name = 'ianlee';
// ian.skill = 'react';

타입단언 as를 쓰게되면 Person 인터페이스를 줘서 ian의 name, skill 타입을 정의하지 않아도 오류가 발생하지 않아 초기값 누락에 대한 에러가 발생할 수 있기 때문에 주의해야 한다.

// non-null type assertion ! 값이있다고 확신
const a: string | null;
a!

마찬가지로 non-null assertion " ! "을 이용해 값이 있다고 해버리면 보이지 않는 사각지대에서 에러가 일어날 수 있다.

그러므로 타입에 대한 확신이 있을때 이용하는 것을 권장한다.

 

옵셔널 체이닝 연산자 ?

 

Type 'null' is not assignable to type 'string'. ts(2322)는 null대신 빈문자열 ''을 넣어주면 해결된다.

Object is possibly 'null'.ts(2531) 는 해결하는 방법이 여러가지가 있다.

// 1. Object가 null일수도 있으니 조건문을 이용해서 해결
function clearDeathList() {
  if (!deathsList) {
    return;
  }
  deathsList.innerHTML = '';
}

// 2. 옵셔널 체이닝 연산자 ? 사용
recoveredList?.appendChild(li);

// 위와 밑의 조건문은 같은 의미다
if (recoveredList === null || recoveredList === undefined) {
  return;
} else {
  recoveredList.appendChild(li);
}

// 3. Element 타입 정하기
const rankList = $('.rank-list') as HTMLOListElement;
const deathsList = $('.deaths-list') as HTMLOListElement;
const recoveredList = $('.recovered-list') as HTMLOListElement;

// index.html에서 <ol>에 있으므로 HTMLOListElement로 단언한다

우선순위는 !보다는 ?를 쓰는 것이 좋다.

 

DOM 유틸 함수

 

DOM 접근 유틸함수 접근하기 위해 $를 붙여주고 타입단언을 이용하였다.

반환값이 Element 또는 null이므로 null이 아니라고 선언해줘야 했고 타입단언을 지정해줘야했다.

// utils
// 아래 DOM에서 document.querySelector('.confirmed-total') 대신 $('.confirmed-total')로 줄여쓸 수 있다
function $(selector: string) {
  return document.querySelector(selector);
}

// DOM
// let a: Element | HTMLElement | HTMLParagraphElement;
const confirmedTotal = $('.confirmed-total') as HTMLSpanElement; // 타입단언
const deathsTotal = $('.deaths') as HTMLParagraphElement;

제네릭을 통해 as를 없애고 구체적인 DOM함수 클래스 타입을 넘길 수 있게 한다.

HTMLElement라는 제약조건을 통해 호환이 될 수 있는 하위 타입들만 들어갈 수 있게한다.

// utils
// 제네릭타입 default값을 HTMLDivElement으로 기본값설정 Div면 타입넘길 필요 없어짐
function $<T extends HTMLElement = HTMLDivElement>(selector: string) {
  const element = document.querySelector(selector);
  return element as T;
}

// DOM
const confirmedTotal = $<HTMLSpanElement>('.confirmed-total');
const deathsTotal = $<HTMLParagraphElement>('.deaths');
반응형