이펙티브 타입스크립트(1)
1-1. 타입스크립트와 자바스크립트의 관계 이해하기
타입스크립트는 자브스키릅트의 상위집합 (superset)이다
타입스크립트는 타입이 정의된 자바스크립트의 상위집합이다
•
모든 자바스크립트 프로그램이 타입스크립트라는 명제는 참이지만, 반대는 성립하지 않는다
•
타입스크립트 컴파일러는 타입스크립트뿐만 아니라 일반 자바스크립트 프로그램에도 유용하다
•
타입스크립트의 목표 중 하나는 런타임에 오류를 발생시킬 코드를 미리 찾아내는 것이다
◦
타입스크립트가 정적 타입 시스템이라는 것은 바로 이런 특징을 말하는것이다
◦
타입 체커가 모든 오류를 찾아내지는 않는다
요약
•
타입스크립트는 자바스크립트 런타임 동작을 모딜렝하는 타입 시스템을 가지고 있기 때문에 런타임 오류를 발생시키는 코드를 찾아내려고 한다
◦
하지만, 타입 체커를 통과하면서도 런타임 오류를 발생시키는 코드는 충분히 존재할 수 있다
•
타입스크립트 타입 시스템은 전반적으로 자바스크립트 동작을 모델링한다
◦
잘못된 매개변수 개수로 함수를 호출하는 경우처럼, 자바스크립트에서 허용되지만 타입스크립트에서는 문제가 되는 경우도 있다
◦
문격의 엄격함은 온전히 취향의 차이이며, 우열을 가릴 수 없는 문제이다
1-2. 타입스크립트 설정 이해하기
•
타입스크립트 설정은 현재 시점으로 거의 100개에 이르는데, 가급적 tsconfig.json 파일로 설정을 관리하는것이 좋다
•
noImplicitAny 는 변수들이 미리 정의된 타입을 가져야 하는지 여부를 제어한다
◦
타입스크립트는 타입 정보를 가질 때 가장 효과적이기 때문에, 가급적 noImplicitAny 를 설정해야 한다
•
strictNullChecks 는 null 과 undefined 가 모든 타입에서 허용되는지 확인한다
◦
strictNullChecks 는 null 과 undefined 관련된 오류를 잡아 내는데 많은 도움이 되지만, 코드 작성을 어렵게 만든다
▪
새 프로젝트를 시작한다면 가급적 설정하는것이 좋지만, 타입스크립트가 처음이거나 자바스크립트 코드를 마이그레이션하는 중이라면 설정하지 않아도 괜찮다
•
any 타입을 매개변수에 사용하면 타입 체커는 속절없이 무력해진다
◦
any 타입은 유용하지만 매우 주의해서 사용해야 한다
1-3. 코드 생성과 타입이 관계없음을 이해하기
•
타입스크립트 컴파일러가 하는 역할
◦
최신 타입스크립트/자바스크립트를 브라우저에서 동작할 수 있도록 구버전의 자바스크립트로 트랜스파일(transpile) 한다
◦
코드의 타입 오류를 체크한다
•
타입스크립트 컴파일러가 하는 역할은 완벽히 독립적으로 수행된다
타입 오류가 있는 코드도 컴파일이 가능하다
코드에 오류가 있을 때 컴파일에 문제가 있다 가 있지만, 이는 기술적으로 틀린말이다.
→ 작성한 타입스크립트가 유효한 자바스크립트라면 타입스크립트 컴파일러는 컴파일을 해낸다.
→ 코드에 오류가 있을 때 타입 체크에 문제가 있다 라고 말하는게 더 정확한 표현이다
•
컴파일은 타입 체크와 독립적으로 동작하기 때문에, 타입 오류가 있는 코드도 컴파일이 가능하다
•
타입 오류가 있는 데도 컴파일된다는 사실 때문에 타입스크립트가 엉성한 언어처럼 보일 수 있지만, 실제로 산출물이 나는 것이 실제로 도움이 될 때가 있다
•
만약 오류가 있을 때 컴파일하지 않으려면, noEmitOnError 를 설정하거나 빌드 도구에 동일학게 적용하면 된다
런타임에는 타입 체크가 불가능하다
•
타입스크립트의 타입은 제거 가능(erasable) 하다.
◦
자바스크립트로 컴파일되는 과정에서 모든 인터페이스, 타입, 타입 구문은 제거된다
•
타입 정보를 유지하는 방법으로는 런타임에 접근 간으한 타입 정보를 명시적으로 저장하는 태그 기법이 있다
예시 코드
◦
위 예시코드에서 Shape 는 태그된 유니온(tagged union) 의 예시이다
◦
이 기법은 런타임에 타입 정보를 손쉽게 유지할 수 있기 때문에, 타입스크립트에서 흔하게 볼 수 있는 기법이다
•
타입 (런타임 접근 불가) 와 값 (런타임 접근 가능) 을 둘 다 사용하려면 타입을 클래스로 만들면 된다
예시 코드
•
인터페이스는 타입으로만 사용 가능하지만, 클래스로 선언하면 타입과 값으로 모두 사용할 수 있다
타입 연산은 런타임에 영향을 주지 않는다
as {type} 은 타입 단언문이다.
•
잘못된 예시
◦
타입스크립트
function asNumber(val: number | string): number {
return val as number;
}
TypeScript
복사
◦
자바스크립트
function asNumber(val) {
return val
}
TypeScript
복사
•
위의 잘못된 예시처럼 타입 체커는 통과하지만, 실제로 컴파일 해보면 런타임 동작에는 아무런 영향을 미치지 않는다.
•
값을 정제하기 위해서는 런타임의 타입을 체크해야 하고, 자바스크립트 연산을 통해 변환을 수행해야 한다
런타임 타입은 선언된 타입과 다를 수 있다
•
타입스크립트에서는 런타임 타입과 선언된 타입이 맞지 않을 수 있다
•
타입이 달라지는 혼란스러운 상황을 가능한 피해야 한다.
◦
선언된 타입이 언제든지 달라질 수 있다는 것을 명심해야 한다.
예시 코드
타입스크립트 타입으로 함수를 오버로드 할 수 없다
함수 오버로딩이란?
→ 동일한 이름에 매개변수만으로 다른 여러 버전의 함수를 사용하는것을 말한다.
•
타입스크립트에서는 타입과 런타임의 동작이 무관하기 때문에, 함수 오버로딩은 불가능하다
•
타입스크립트가 함수 오버로딩 기능을 지원하기는 하지만, 온전히 타입 수준에서만 동작한다
◦
하나의 함수에 대해 여러 개의 선언문을 작성할 수 있지만, 구현체(implementation) 은 오직 하나 뿐이다
•
타입스크립트에서는 타입 정보를 제공할 뿐이며, 실제 컴파일되면서 제거되고 구현체만 남게된다
예시 코드
타입스크립트 타입은 런타임 성능에 영향을 주지 않는다
•
타입과 타입 연산자는 컴파일 시점에 제거되기 때문에, 런타임의 성능에 아무런 영향을 주지 않는다
•
런타임 오버헤드가 없는 대신, 타입스크립트 컴파일러는 빌드타임 오버헤드가 있다
◦
타입스크립트 팀은 컴파일러 성능을 매우 중요하게 생각한다
◦
컴파일은 일반적으로 상당히 빠른 편이며, 증분(incremental) 빌드 시 더욱 체감된다
◦
오버헤드가 커지면, 빌드 도구에서 트랜스파일만 하는 옵션(transpile only)을 설정하여 타입 체크를 건너뛸 수 있다
•
타입스크립트가 컴파일하는 코드는 오래된 런타임 환경을 지원하기 위해 호환성을 높이고 성능 오버헤드를 감안할지, 호환성을 포기하고 성능 중심의 네이티브 구현체를 선택할 지의 문제에 맞닥뜨릴 수도 있다
◦
제네레이터 함수가 ES5 타깃으로 컴파일되면, 타입스크립트 컴파일러는 호환성을 위해 특정 헬퍼 코드를 추가한다
▪
제네레이터의 호환성을 위한 오버헤드 또는 성능을 위한 네이티브 구현체의 선택의 문제이다
◦
호환성과 성능 사이의 선택은 컴파일 타깃과 언어 레벨의 문제이며, 타입과 무관하다
1-4. 구조적 타이핑에 익숙해지기
덕 타이핑이란?
→ 객체가 어떤 타입에 부합하는 변수와 메서드를 갖리 경우 객체를 해당 타입에 속하는 것으로 간주하는 방식이다
•
자바스크립트는 본질적으로 덕 타이핑(duck typing) 기반이다.
•
타입스크립트는 매개변수 값이 요구사항을 만족한다면 타입이 무엇인지 신경쓰지 않는 동작을 그대로 모델링한다.
예시 코드
•
구조적 타이핑 (structural typing) 때문에 문제가 발생할 수 있다
예시 코드
•
함수를 작성할 때, 호출에 사용되는 매개변수의 속성들이 매개변수의 타입에 선언된 속성만을 가질 거라 생각하기 쉽다.
◦
이러한 타입은 봉인된 (sealed) 또는 정확한 (precise) 타입이라고 불리며, 타입스크립트 타입 시스템에서는 표현할 수 없다
◦
타입스크립트 타입은 open 상태이다
▪
open 은 타입 확장에 열려있다는 의미이다
▪
타입에 선언된 속성 외에 임의의 속성을 추가하더라도 오류가 발생하지 않는다는것을 의미한다
예시 코드
•
구조적 타이핑은 클래스와 관련된 할당문에서도 당황스러운 결과를 보여준다
예시 코드
정리
•
자바스크립트가 덕 타이핑 기반이고, 타입스크립트가 이를 모델링하기 위해 구조적 타이핑을 사용한다
◦
어떤 인터페이스에 할당 가능한 값이라면 타입 선언에 명시적으로 나열된 속성들을 가지고 있다
◦
타입은 ‘봉인'되어 있지 않다
•
클래스 역시 구조적 타이핑 규칙을 따른다
◦
클래스의 인스턴스가 예상과 다를 수 있다
•
구조적 타이핑을 사용하면 유닛 테스팅을 쉽게 할 수 있다
1-5. any 타입 지양하기
•
타입스크립트의 타입 시스템은 점진적(gradual) 이고 선택적(optional) 이다
•
일부 특별한 경우를 제외하고는 any 를 사용하면 타입스크립트의 장점을 누릴 수 없다
•
any 타입에는 타입 안정성이 없다
•
any 는 함수 시그니처를 무시한다
◦
자바스크립트에서는 종종 암시적으로 타입이 변환되기 때문에 문제가 발생할 수 있다
예시 코드
•
any 타입에는 IntelliSense 가 동작하지 않는다
◦
타입스크립트의 모토는 확장 가능한 자바스크립트 이다
▪
any 를 사용하게 되면 언어 서비스를 제대로 누릴 수 없다
•
any 타입은 코드 리팩토링 때 버그를 감춘다
예시 코드
•
any 는 타입 설계를 감춘다
◦
애플리케이션 상태 같은 객체를 정의하려면 꽤 복잡하지만 any 를 사용하면 간단하게 해결해버릴 수 있다.
◦
하지만, any 를 사욯아게 되면 상태 객체의 설계 자체를 감춰버리기 때문에 문제가 발생할 숭 ㅣㅅ다.
◦
any 를 사용하면 타입 설계가 불분명해진다
•
any 는 타입시스템의 신뢰도를 떨어뜨린다