[이펙티브 타입스크립트] 타입 공간과 값 공간의 심벌 구분하기
반응형
타입스크립트의 심벌(symbol)은 타입 공간이나 값 공간 중의 한 곳에 존재한다.
심벌은 이름이 같더라도 속하는 공간(타입 or 값)에 따라 다른 것을 나타낼 수 있기 때문
// Cylinder은 타입으로 사용
interface Cylinder {
radius: number;
height: number;
}
// Cylinder로 이름은 같지만 값으로 사용
const Cylinder = () => (radius: number, height: number) => ({ radius, height });
// => Cylinder는 상황에 따라 타입 or 값으로 둘다 사용될 수 있음
function calcVolume(shape: unknown) {
if(shape instanceof Cylinder) {
console.log(shape.radius); // 오류
}
}
instanceof를 이용하여 shape가 Cylinder 타입인지 체크해보자는 의도였지만 중요한 점은 instanceof는 자바스크립트의 런타임 연산자 → 값에 대한 연산을 수행하기 때문에 Cylinder 함수를 참조하게 된다.
그렇다면 타입과 값은 어떻게 구분할 수 있는가? 이는 조금 혼란스러울 때가 많다.
type T1 = 'string literal';
type T2 = 123;
const V1 = 'string literal';
const V2 = 123;
- 일반적으로 type, interface 다음에 오는 심벌은 타입
- const, let 선언에 쓰이는 건 값
typescirpt 플레이그라운드를 활용하기
- ts 소스로부터 변환된 js 결과물을 보여줌
- 컴파일 과정에서 타입 정보는 제거되기 때문에, 심볼이 사라졌다면 그것은 타입에 해당
타입과 값 구별해보기
- 앞선 규칙에 추가
- 타입 선언 :, as 다음에 오는 심벌은 타입
- = 다음에 오는 값은 무조건 값
interface Person {
first: string;
last: string;
}
const p: Person = { first: 'Jane', last: 'Jacobs' };
function email(p: Person, subject: string, body: string): Response {
// ...
}
상황에 따라 type과 값 모두 가능한 예약어 class
class Cylinder {
radius=1;
height=1;
}
function calcVolume(shape: unknown) {
if(shape instanceof Cylinder) {
console.log(shape);
console.log(shape.radius);
}
}
Cylinder 클래스는 타입으로 사용 (값으로 쓰일 때는 생성자가 사용되는 경우)
타입과 값에서 다른 기능을 하는 typeof 연산자
type T1 = typeof p; // Person 타입이라고 가정
// type은 (p: Person, subject: string, body: string) => Response
type T2 = typeof email;
const v1 = typeof p; // 값은 "object"
const v2 = typeof email; // 값은 "function"
타입의 관점
- typeof는 값을 읽고 ts 타입을 리턴
- 타입 공간의 typeof는 큰 타입의 일부분으로 사용
- type 구문으로 type에 이름을 붙이는 용도로도 사용
값의 관점
- js 런타임의 typeof 연산자
- 값 공간의 typeof는 문자열을 반환
js 런타임의 타입은 ts의 정적 타입 시스템보다 훨씬 간단하다.
- js 런타임의 타입: number / boolean / undefined / object / function
클래스에 대한 typeof는 상황에 따라 다르게 동작한다.
const v = typeof Cylinder; // 값이 "function"
type T = typeof Cylinder; // ??
declare let fn: T;
const c = new fn(); // 타입이 Cylinder
// InstanceType 제너릭을 사용해 생정자 타입과 인스턴스 타입을 전환할 수 있음
type C = InstanceType<typeof Cylinder>;
declare let smallC: C;
- 클래스가 js런타임에서 실제 함수로 구현되기 때문에 v는 “function”
- 중요한 점은 Cylinder은 인스턴스 타입이 아니라는 점, (=생성자 함수)
속성선택자 [ ]
const first: Person['first'] = p['first'];
// 인덱스 위치에는 유니온 타입과 기본형 타입을 포함한 어떤 타입이든 사용 가능
type PersonEl = Person['first' | 'last']; // string
type Tuple = [string, number, Date];
type TupleEl = Tuple[number]; // string | number | Date
속성선택자 []는 타입으로 쓰일 때에도 값에서 사용하는 것과 동일하게 동작
하지만 obj[’filed’]와 obj.filed는값이 동일하더라도 타입은 다를 수 있음 (?) → 타입의 속성을 얻을 때에는 반드시 obj[’filed’]를 사용해야 한다 (속성 선택자에 대한 내용은 아이템 14에서 더 자세히 다룰 예정)
두 공간(타입, 값)사이에서 다른 의미를 가지는 코드 패턴들
- this 키워드 (아이템 49)
- 값으로 쓰이는 this: 자바스크립트의 this 키워드
- 타입으로 쓰이는 this: 다형성(polymorphic) this 라고 불리는 this → 서브클래스의 메서드 체인 구현할 때 유용
- &와 |
- 값: AND 와 OR 비트 연산자
- 타입: 인터섹션과 유니온
- const
- 값: 새 변수 선언
- 타입: as const는 리터럴 혹은 리터럴 표현식의 추론된 타입을 변경(아이템 21)
- extends
- 서브 클래스(class A exnteds B)
- 서브 타입(interface A extends B)
- 제너릭 타입의 한정자(Generic<T extends number>)
- in
- 루프(for (key in object)) 또는 매핑된(mapped) 타입
function email(options: { person: Person, subject: string, body: string }) {
// ...
}
function email({
person: Person,
subject: string,
body: string,
}) {
// ...
}
- 'Person' is declared but its value is never read.ts(6133)
- Duplicate identifier 'string'.(2300) Binding element 'string' implicitly has an 'any' type.(7031)
→ 값의 관점에서 Person과 string이 해석되었기 때문에 오류 발생
// 타입과 값을 구분해야 한다.
function email({
person, subject, body
}): { persone: Person, subject: string, body: string } {
// ...
}
요약
- ts 코드를 읽을 때 타입인지 값인지 구분하는 방법을 터특하자 (타입스크립트 플레이그라운드 활용하여 개념잡자)
- 모든 값은 타입을 가지지만, 타입은 값을 가지지 않는다. type과 interface 같은 키워드는 타입 공간에만 존재
- class나 enum 같은 키워드는 타입과 값 두가지로 사용될 수 있다.
- “foo”는 문자열 리터럴이거나, 문자열 리터럴 타입일 수 있다. 차이점을 알고 구별하는 방법을 터득하자.
- typeof, this 그리고 많은 다른 연산자들과 키워드들은 타입 공간과 값 공간에서 다른 목적으로 사용될 수 있다.
반응형
'프로그래밍 > Typescript' 카테고리의 다른 글
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기 (0) | 2022.06.10 |
---|---|
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기 (0) | 2022.06.05 |
[TypeScript] e.target.target 타입 에러가 나는 이유 ts(2339) (0) | 2022.06.04 |
댓글
이 글 공유하기
다른 글
-
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기
[이펙티브 타입스크립트] 객체 래퍼 타입 피하기
2022.06.10 -
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기
[이펙티브 타입스크립트] #4 구조적 타이핑에 익숙해지기
2022.06.05 -
[TypeScript] e.target.target 타입 에러가 나는 이유 ts(2339)
[TypeScript] e.target.target 타입 에러가 나는 이유 ts(2339)
2022.06.04