본 시리즈에서는 책 "이펙티브 타입스크립트"를 읽고 내용을 정리할 예정입니다.
이 책에서는 타입스크립트란 무엇이고, 어떻게 여겨야 하는지, JS와 어떤 관계인지, TS의 타입들은 null이 가능한지, any 타입에서는 어떻게 동작하는지, 덕 타이핑이 가능한지 등을 알아 볼 예정이다.
Typescript는 독특한 언어이다. 파이썬이나 루비와 같이 인터프리터로 실행되는 것도 아니고, C나 Java 처럼 저수준 언어로 컴파일되는 것도 아니다.
Typescript는 고수준 언어인 Javascript로 컴파일되며, Javascript로 실행된다. 이처럼 TS와 JS의 관계는 매우 필연적이다.
타입스크립트와 자바스크립트의 관계 이해하기
"타입스크립트는 자바스크립트의 상위 집합이다" 또는 "타입스크립트는 타입이 정의된 자바스크립트의 상위 집합이다." 라는 말을 들어본 적이 있을 것이다. 이러한 이야기가 나오는 이유는 그만큼 타입스크립트가 자바스크립트와 굉장히 밀접한 관계에 있기 때문이다.
타입스크립트는 문법적으로 자바스크립트의 상위집합이다. JS 프로그램에 문법 오류가 없다면 유효한 TS 프로그램이라고 할 수 있지만, JS 프로그램에 이슈가 있다면 문법 오류가 아니라도 타입 체커에 의해 지적당할 가능성이 높다.
하지만, 문법의 유효성과 동작의 이슈는 독립적인 문제이다. 이는 아이템 3에서 다룰 예정이다.
JS 파일이 .js 또는 .jsx 확장자를 사용하는 반면, TS 파일은 .ts 또는 .tsx 확장자를 사용한다.
TS는 JS의 상위 집합이기 때문에 .js 파일에 있는 코드는 이미 TS라고 할 수 있으며, main.js를 main.ts 처럼 확장자를 바꿔도 달라지는 것은 없다. 이러한 특징은 JS코드를 유지하며 그대로 파일의 일부분에 TS를 적용할 수 있어서 마이그레이션 작업에 큰 이점을 가져온다.
주의해야할 점은 모든 JS 프로그램이 TS 프로그램이라는 명제는 참이지만, 모든 TS 프로그램은 JS 프로그램이라는 것은 성립되지 않는다. 이는 TS가 타입을 명시하는 추가적인 문법을 갖기 때문이다.
타입 추론을 이용한 오류 발견
function greet(who: string){
console.log('Hello', who);
}
위의 코드는 유효한 TS 프로그램이지만, JS를 구동하는 Node 같은 프로그램으로 위의 코드를 실행하면 오류를 출력한다.
function greet(who: string){
console.log('Hello', who);
}
SyntaxError: Unexpected token :
: string은 타입스크립트에서 쓰이는 타입 구문이며, 타입 구문을 사용하는 순간부터 JS는 TS 영역으로 들어가게 된다.
TS 컴파일러는 TS 뿐만 아니라 JS 프로그램에도 유용하다.
let city = 'new york city';
console.log(city.toUppercase());
위의 코드를 .ts 파일에 작성하게 된다면 코드에 타입 구문이 없지만 TS의 타입 체커는 문제점을 찾아낸다.
TS에서 중요한 부분인 타입 추론을 통해 city 변수가 문자열이라는 것을 명시하지 않아도 초기값으로부터 타입을 추론한다.
명시적 타입 선언으로 정확한 오류 찾기
TS의 타입 시스템의 목표 중 하나는 런타임에 오류를 발생시킬 코드를 미리 찾아내는 것이다. 명시적으로 타입을 선언해 정적으로 타입을 관리하는 이유도 런타임 이전에 오류를 미리 찾기 위함이다.
하지만 타입 체커가 모든 오류를 찾아주지 않고, 런타임에 오류는 발생하지 않지만 코드의 의도와는 달리 동작하는 코드도 있다.
const states = [
{name: 'Alabama', capital: 'Montgomery'},
{name: 'Alaska', capital: 'Juneau'},
{name: 'Arizona', capital: 'Phonenix'},
];
for (const state of states){
console.log(state.capitol);
}
위의 코드는 유효한 JS 혹은 TS 코드이며, 오류 없이 실행 가능하다. 하지만 TS의 타입 체커는 추가적인 타입 구문 없이도 오류를 찾는다.
for (const state of states){
console.log(state.capitol);
// 'capitol' 속성이 ... 형식에 없습니다.
// 'capital' 을(를) 사용하시겠습니까?
}
TS는 타입 구문 없기 오류를 찾을 수 있지만, 타입 구문을 추가하면 더 많은 오류를 찾을 수 있으며 코드의 의도를 파악해 코드의 동작과 의도가 다른 부분을 찾을 수 있다.
하지만 항상 정확한 오류의 원인을 추측할 수 있는 것은 아니기에 명시적으로 states를 선언해 의도를 분명히 하는 것이 더 좋다.
interface State {
name: string;
capital:string;
}
const states: State[] = [
{name: 'Alabama', capitol: 'Montgomery'},
{name: 'Alaska', capitol: 'Juneau'},
{name: 'Arizona', capitol: 'Phonenix'},
];
// 객체 리터럴은 알려진 속성만 지정할 수 있지만 'State'형식에 'capitol' 이(가) 없습니다.
// 'capital'을(를) 쓰려고 했습니까?
for (const state of states){
console.log(state.capitol);
}
명시적으로 타입을 선언해주면 의도를 명시적으로 알려주며 TS가 잠재적으로 문제점을 찾을 수 있게 되었다.
TS는 JS의 상위집합이라는 말이 사실 타입체커를 통과한 TS 프로그램 영역 덕분에 이질감이 든다....
보통 타입 체크에서 오류가 발생하지 않도록 주의하며 TS 코드를 작성하기 때문이다.
JS 런타임 동작을 모델링하는 TS 타입 시스템
TS 타입 시스템은 JS 런타임 동작을 모델링한다.
const x = 2 + '3'; // 정상, string 타입입니다.
const x = '2' + 3; // 정상, string 타입입니다.
위의 예제는 TS의 타입 체커는 정상으로 인식합니다. 이는 JS의 런타임 동작과 같이 TS 타입 시스템이 동일하게 동작하는 것을 알 수 있습니다.
하지만 JS에서는 정상 동작하지만(런타임 오류 발생 X) 타입 체커가 문제점을 표시하는 경우도 있다.
const a = null + 7;
// JS에서는 7이 되지만, TS에서는 오류가 발생한다.
const b = [] + 12;
// JS에서는 12가 되지만, TS에서는 오류가 발생한다.
alert('Hello', 'Typescript');
// JS에서는 Hello가 출력 되지만,
// TS에서는 alert에 전달할 인수의 개수가 넘어 에러 발생.
TS 타입 시스템이 JS의 런타임 동작을 모델링 하는 것은 기본 원칙이지만, 단순히 런타임 동작을 모델링하는 것뿐만 아니라 의도하지 않은 코드가 오류로 이어질 수 있다는 점까지 고려해야 한다. 프로그램에 오류가 발생하지 않더라도 타입 체커가 오류를 표시한다.
TS는 취향 차이
JS 런타임 동작을 모델링하는 시기 또는 추가적인 타입 체크의 시기가 분명하지 않다면 TS를 사용해도 되는지 의문이 들 수 있다.
그러나 null과 7을 더하거나, []과 12를 더하거나, 불필요한 매개변수를 추가해 함수 호출하는 것을 당연하게 사용할 예정이라면 TS 사용은 비추천한다.
또한 타입 체크를 통과해도 런타임에 오류가 발생할 수 있다.
const names = ['Alice', 'Bob'];
console.log(names[2].toUpperCase());
// TypeError: Cannot read property 'toUpperCase' of undefined
any 타입을 사용할 때도 예상치 못한 오류가 자주 발생한다.
앞서 등장한 오류들이 발생하는 근본 원인은 TS가 이해하는 값의 타입과 실제 값에 차이가 있기 때문이다. 타입 시스템이 정적 타입의 정확성을 보장해 줄 것 같지만 그렇지 않으며 애초에 그런 목적으로 만들어지지 않았다.
요약
- 타입스크립트는 자바스크립트의 상위 집합이다. 모든 자바스크립트 프로그램은 이미 타입스크립트 프로그램이지만, 타입스크립트는 별도의 문법이 있기에 일반적으로 유효한 자바스크립트 프로그램이 아니다.
- 타입 스크립트는 자바스크립트 런타임 동작을 모델링하는 타입 시스템을 갖고 있기 때문에 런타임 오류를 발생시키는 코드를 찾는다. 하지만, 타입 체커를 통과해도 런타임 오류가 발생할 수 있기에 맹신하면 안된다.
- 타입스크립트 타입 시스템은 전반적으로 자바스크립트 동작을 모델링한다. 그러나 자바스크립트에서는 허용되지만 타입 스크립트에서는 문제가 될 수 있기에 문법의 엄격함은 타입스크립트의 사용은 취향차이로 생각된다.
'TypeScript > 이펙티브 타입스크립트' 카테고리의 다른 글
아이템 3 코드 생성과 타입이 관계없음을 이해하기 (1) | 2024.09.13 |
---|---|
아이템2 타입스크립트 설정 이해하기 (0) | 2024.09.03 |