이펙티브 타입스크립트 스터디(1)

오형근·2022년 8월 25일
0

Typescript

목록 보기
8/15
post-thumbnail

Effective Typescript에 대한 스터디 진행 내용을 요약한 것입니다.

TS와 JS간의 관계 이해하기

  • 타입스크립트는 자바스크립트로 컴파일된 후 실행되는 언어이기 때문에, 둘 사이의 관계를 잘 이해해야 좋은 타입스크립트 개발자로 성장할 수 있다.

TS의 타입 시스템

TS는 JS의 상위호환(Superset)이다.

  • JS에 문법적 오류가 없다면 TS에서도 실행할 수는 있다. 또한 TS에서 오류라고 판명이 되어도 JS로 컴파일 및 실행이 될 수 있다.

차이점

그럼에도 TS와 JS에는 명확한 차이점이 존재한다.

TS는 다음과 같이 타입을 명시해줄 수 있다.

function greet(who: string) {
	console.log("Hello", who)
}

greet("jn")

위에서 :string은 JS에는 없는 문법으로, 이처럼 타입을 사용함으로써 JS는 TS의 영역으로 들어가게 된다.

let city = 'new york city'
console.log(city.toUppercase()) // 'toUppercase' 속성이 'string' 형식에 없습니다. 'toUpperCase'를 사용하시겠습니까?

TS는 위의 예시에서 문제점을 찾아낸다.
이는 TS가 자체적으로 city의 타입을 string으로 추론한 결과이다.

이와 같은 타입 추론은 TS의 중요한 특징이다.
타입 추론을 통해 TS는 컴파일 전에 많은 오류를 수정할 수 있도록 도와준다.

const status = [
  { name: "Alabama", capital: "Montgomery" },
  { name: "Alaska", capital: "Juneau" },
  { name: "Arizona", capital: "Phoenix" },
  //...
]
for (const state of status) {
	console.log(state.capitol) // 'capitol' 속성이 '{ name: string, capital: string }'형식에 없습니다. 'capital'을 사용하시겠습니까?
}

위처럼 TS는 추가적인 타입 구문이 없이도 오류를 찾아낸다.
코드의 의도가 무엇인지 파악하고 이에 따른 오류를 잡아낸다는 것이다.
이를 통해 우리는 코드와 의도가 다른 부분을 손쉽게 찾을 수 있다.

하지만 이 또한 언제나 정확하게 의도와 들어맞을 수는 없기 때문에, 타입 체커가 원활하게 동작하기 위해서 TS는 interface를 정의할 것을 권장하고 있다.

interface State {
	name: string;
  	capital: string;
}
const status: State[] = [ // State interface를 구현한 것들로 이루어진 배열이다.
  { name: "Alabama", capital: "Montgomery" },
  { name: "Alaska", capital: "Juneau" },
  { name: "Arizona", capital: "Phoenix" },
  //...
]
for (const state of status) {
	console.log(state.capitol) // '{ name: string; capitol: string; }' 형식은 'State' 형식에 할당할 수 없습니다. 개체 리터럴은 알려진 속성만 지정할 수 있짐나 'State' 형식에 'capitol'이(가) 없습니다. 'capital'을(를) 쓰려고 했습니까?
}

위처럼 interface를 미리 정의해둠으로써 TS가 작성자의 의도를 정확히 파악할 수 있도록 하였다. 이는 TS가 정확한 오류를 명시할 수 있도록 만들어준다.

다음의 예시를 보자.

const x = 2 + "3" // OK, type is string
const y = "2" + 3 // OK, type is string

위 코드는 일반적인 언어에서는 비정상적인 동작을 할 코드이다. 그러나 TS에서는 자동으로 타입을 추론하기 때문에 위의 코드 같은 경우도 자동으로 형변환이 이루어져 잘 동작한다.

다만 아래 코드처럼 JS에서만 인정되고 TS에서는 반려되는 일도 있다.

const a = null + 7 // Evaluates to 7 in JS
const b = [] + 12 // Evaluates to '12' in JS\
alert("Hello", "Typescript") // alerts "Hello"

위의 세 문장은 JS에서는 자동 형변환을 통해 실행이 되나, TS 내부적으로 타입 오류로 판정된다.

위와 같은 경우가 많지는 않지만, 우리는 JS의 잘못된 자료형끼리의 연산을 방치해둘 수 없기 때문에 TS를 사용하는 것이 권장된다. 물론 사용 여부는 온전히 개발자의 선택이다(책에서 필수적이라고 언급하지 않았다).

TS Config 이해하기

  • TS에서 사용되는 설정 중 자주 보고 중요한 것들을 나열해보자.

noImplicitAny

  • 타입에 대한 아무런 명시가 되어있지 않은 자료에 대해서 TS는 기본적으로 any 타입으로 추론한다. 그러나 noImplicitAny 설정이 true라면, any를 이용한 타입 추론을 시도하지 않고 바로 에러를 발생시킨다. false라면 타입에 대한 명시가 되어있지 않은 자료라도 에러를 발생시키지 않고 암묵적으로 any로 타입을 추론한다.

프로젝트를 처음 시작한다면 처음부터 noImplicitAnytrue로 설정하여 개발자로 하여금 타입을 명시하도록 해야한다. 그러면 TS가 문제를 발견하기 쉬워지고, 코드의 가독성이 좋아지며, 개발자의 생산성이 향상됩니다. JS로 작성된 레거시 코드를 TS로 마이그레이션할 때 유용하다.

strictNullChecks

  • nullundefined가 모든 타입에서 허용되는지에 대한 설정이다.
const x: number = null

위의 코드는 strickNullCheckstrue라면 에러를 발생시키고, false라면 에러를 발생시키지 않는다.

const x: number | null = null

위 코드처럼 null을 명시해줌으로써 null에 대한 타입 추론 오류 방지를 할 수 있다.

const el = document.getElementById('status') // Object is possibly 'null'

if(el) {
	el.textContent = 'Ready' // OK, null has been excluded
}
el!.textContent = 'Ready' // OK, we've asserted that el is non-null

혹은 위 코드처럼 if문을 사용하여 el이 무조건 존재한다는 조건을 걸어주거나, !를 사용하여 이 자료가 무조건 존재할거라고 TS에게 강제성을 더해줄 수 있다.

strict

아마 기본 설정으로 이 설정이 들어갔을 확률이 높은데, 위의 두 설정을 한번에 포괄하는 설정이다.

strict가 true라면 noImplicitAnystrictNullChecks가 모두 true인 상황이고, false라면 그 반대인 것이다.

아주 엄격하거나, 아주 느슨하거나 라고 생각하면 좋을 것 같다.

profile
eng) https://medium.com/@a01091634257

0개의 댓글