Blog

이펙티브 타입스크립트(2-1)

이펙티브 타입스크립트(2)

2-1. 편집기를 사용하여 타입 시스템 탐색하기

타입스크립트 컴파일러 (tsc)
단독으로 실행할 수 있는 타입스크립트 서버 (tsserver)
코드 자동 완성
명세(사양, specification) 검사
검색
리팩토링

2-2. 타입이 값들의 집합이라고 생각하기

타입스크립트에서 가장 작은 집합은 아무 값도 포함하지 않는 공집합이며, 이를 never 라고 부른다
never 타입은 어떠한 값도 할당할 수 없다
never 다음으로 작은 집합은 한 가지 값만 포함하는 타입이고, 이를 unit 타입이라고 불리는 literal 타입이다
literal 을 여러개로 묶으려면 union 타입을 사용한다
예시 코드
extends 키워드는 제네릭 타입에서 한정자로도 사용되며, ~의 부분 집합 이라는 의미를 가지고 있다

정리

타입을 값의 집합으로 생각하면 이해하기 편하다 (타입의 범위)
집합은 유한(boolean 또는 literal)하거나 무한(string 또는 number)하다.
타입스크립트 타입은 엄격한 상속 관계가 아니라 겹쳐져 있는 집합으로 표현된다
두 타입은 서로 서브타입이 아니면서도 겹쳐질 수 있다
한 객체의 추가적인 속성이 타입 선언에 언급되지 않더라도 그 타입에 속할 수 있다
타입 연산은 집합의 범위에 적용된다
객체 타입에서는 A & B 인 값이 A 와 B 의 속성을 모두 가짐을 의미한다.
A 는 B 를 상속, A 는 B 에 할당 가능 , A 는 B 의 서브타입A 는 B 의 부분 집합 과 같은 의미이다\

2-3. 타입 공간과 값 공간의 심벌 구분하기

타입스크립트의 심벌(symbol)은 타입 공간이나 값 공간 중의 한 곳에 존재한다
이름이 같더라도 속하는 공간에 따라 다른 것을 나타낼 수 있기 때문에 혼란을 야기할 수 있다
아래의 예시는 Cylinder 라는 이름은 같지만, 서로 아무런 관련이 없다
일반적으로 type 이나 interface 의 심벌은 타입인 반면, constlet 선언에 쓰이는 것은 값이다
예시 코드
classenum 은 상황에 따라 타입과 값 두 가지 모두 가능한 예약어이다
class 는 타입으로 쓰일 때는 형태 (property, method) 가 사용되는 반면, 값으로 사용될 때는 생성자가 사용된다
예시 코드
typeof와 같이 연산자 중에서도 타입에서 쓰일 때와 값에서 쓰일 때 다른 기능을 하는 것들이 있다
예시 코드
자바스크립트의 런타임 타입 시스템은 타입스크립트 타입과는 다르다
자바스크립트 런타임 타입 시스템은 타입스크립트 정적 타입 시스템보다 훨씬 간단하다
타입스크립트 타입의 종류는 무수히 많은 반면, 자바스크립트는 6개(string, numebr, boolean, undefined, object, function) 의 런타임 타입만 존재한다
자바스크립트 vs 타입스크립트 차이
this
다형성(polymorphic) this 라고 불리는 this 의 타입스크립트 타입이다
서브클래스의 메서드 체인을 구현할 때 유용
& 와 |
자바스크립트: ANDOR 비트연산자
타입스크립트: IntersectionUnion
const
as const 는 리터럴 또는 리터럴 표현식의 추론된 타입을 변경
extends
서브 클래스 또는 서브 타입 또는 제네릭 한정자를 정의
in
반복문 (for (key in object))) 또는 매핑된(mapped) 타입

정리

모든 값은 타입을 가지지만, 타입은 값을 가지지 않는다
typeinterface 는 타입 공간에만 존재한다
classenum 은 타입과 값 두가지 모두 사용할 수 있다
typeof, this 그리고 많은 연산자들과 키워드들은 타입 공간과 값 공간에서 다른 목적으로 사용될 수 있다

2-4. 타입 단언보다는 타입 선언을 사용하기

타입스크립트 변수에 값을 할당하고 타입을 부여하는 방법은 2가지가 있다
타입 단언 (type assertion)
타입 선언 (type annotation)
예시 코드
타입 선언은 할당되는 값이 해당 인터페이스를 만족하는지 검사하지만, 타입 단언은 강제로 타입을 지정해서 타입 에러를 무시한다
타입 단언이 꼭 필요한 경우가 아니라면, 안정성 체크도 되는 타입 선언을 사용하는것이 좋다
예시 코드
not null assertion (!) 은 null 이 아님을 단언하는 경우 사용한다
타입 체커는 nullable 한지 알지 못하지만, 그 값이 null 이 아니라고 확신하는 경우에만 사용해야 한다.
예시 코드
타입 단언문으로 임의의 타입 간에 변환을 할 순 없다
타입이 부분집합 인 경우에 타입 단언문을 사용해 변환할 수 있다
이를 강제로 해결하려면 unknown 을 사용해야 한다
모든 타입은 unknown 의 서브타입이기 때문에 unknown 이 포함된 단언문은 항상 동작한다
예시 코드

정리

타입 단언 보다는 타입 선언을 사용해야 한다
화살표 함수의 반환 타입을 명시하는 방법을 알아야 한다
타입스크립트보다 타입 정보를 더 잘 알고 있는 상황에서는 타입 단언문과 null 아님 단언문을 사용하면 된다

2-5. 객체 래퍼 타입 피하기

자바스크립트에는 객체 이외에도 기본형 값들에 대한 일곱가지 타입이 있다
타입: string, number, boolean, null, undefined, symbol, bigint
string, number, boolean, null 은 자바스크립트 초창기부터 존재했다
symbol 기본형은 ES2015 에 추가되었다
bigint 는 최종 확정 단계에 있다
기본형들은 불변(immutable) 이며 메소드를 가지지 않는다
자바스크립트는 기본형과 객체 타입을 서로 자유롭게 변환한다
string 기본형에 charAt 같은 메소드를 사용할 때, 자바스크립트는 기본형을 String 객체로 래핑하고, 메소드를 호출하고 마지막에 래핑된 객체를 버린다.
String 객체를 직접 생성할 수 있으며, string 기본형처럼 동작한다.
그러나 string 기본형과 String 객체 래퍼가 항상 동일하게 동작하지 않는다
String 객체는 오직 자기자신하고만 동일하다.
예시 코드
nullundefined 는 객체 래퍼가 없다
래퍼 타입들 덕분에 기본형 값에 메소드를 사용할 수 있고, 정적 메소드도 사용할 수 있다
하지만, 래퍼 객체를 직접 생성할 필요는 없다
타입스크립트가 제공하는 타입 선언은 전부 기본형 타입으로 되어 있다
타입스크립트는 기본형 타입을 객체 래퍼로 할당하는 선언을 허용하지만, 이렇게 사용할 경우 오해하기 쉽기 땜누에 기본형 타입을 사용하는것이 낫다

요약

기본형 값에 메소드를 제공하기 위해 객체 래퍼 타입이 어떻게 사용되는지 이해해야 한다
직접 사용하거나 인스턴스를 생성하는것은 피해야 한다
타입스크립트 객체 래퍼 타입은 지양하고, 기본형 타입을 사용해야 한다

2-6. 잉여 속성 체크의 한계 인지하기

타입이 명시된 변수에 객체 리터럴을 할당할 때 타입스크립트는 해당 타입의 속성이 있는지, 그 외의 속성은 없는지 확인한다
예시 코드
잉여 속성 체크를 이용하면 기본적으로 타입 시스템의 구조적 본질을 헤치지 않으면서도 객체 리터럴에 알 수 없는 속성을 허용하지 않는다
엄격한 객체 리터럴 체크 라고도 불린다
잉여 속성 체크는 타입 단언문을 사용할 때에도 적용되지 않는다
타입 단언문보다 타입 선언문을 사용해야 하는 이유 중 하나이다
잉여 속성 체크를 원하지 않는다면, 인덱스 시그니처를 사용해서 타입스크립트가 추가적인 속성을 예상할 수 있도록 할 수 있다
예시 코드
선택적 속성만 가지는 약한(weak) 타입에도 비슷한 체크가 동작한다
예시 코드
잉여 속성 체크는 구조적 타이핑 시스템에서 허용되는 속성 이름의 오타 같은 실수를 잡는데 효과적인 방법이다

요약

객체 리터럴을 변수에 할당하거나, 함수에 매개변수로 전달할 때 잉여 속성 체크가 수행된다
잉여 속성 체크는 오류를 찾는 효과적인 방법이지만, 타입스크립트 타입 체커가 수행하는 일반적인 구조적 할당 가능성 체크와 역할이 다르다
잉여 속성 체크에는 한계가 있다
임시 변수를 도입하면 잉여 속성 체크를 건너뛸 수 있다

2-7. 함수 표현식에 타입 적용하기

자바스크립트와 타입스크립트에서 함수 문장(statement)함수 표현식 (expression) 을 다르게 인식한다.
타입스크립트에서는 함수 표현식을 사용하는것이 좋다
함수의 매개변수부터 반환값까지 전체를 함수 타입으로 선언하여 함수 표현식에서 재사용할 수 있기 때문이다
예시 코드
다른 함수의 시그니처와 동일한 타입을 가지는 새 함수를 작성하거나, 동일한 타입 시그니처를 가지는 여러개이 함수를 작성할 때는 매개변수의 타입과 반환 타입을 반복해서 작성하지 말고 함수 전체의 타입 선언을 적용해야 한다

정리

매개변수나 반환 값에 타입을 명시하기보다는 함수 표현식 전체에 타입 구문을 적용하는것이 좋다
만약 같은 타입 시그니처를 반복적으로 작성한 코드가 있다면 함수 타입을 분리하는게 좋다
다른 함수의, 시그니처를 참조하려면 typeof fn 을 사용하면 된다