아이템7~아이템 8까지 정리
타입스크립트의 타입 공간, 값 공간
- 타입스크립트의 심벌은 타입 공간이나 값 공간 중 한 곳에 존재한다.
- 이름이 같더라도 속하는 공간에 따라 다른 것을 나타낼 수 있다.
- 예제의 interface Cylinder와 const Cylinder는 아무런 관련이 없다. 상황에 따라서 타입으로 쓰일 수도 값으로 쓰일 수도 있다.
- 하지만 이런 점이 가끔 오류를 발생시킨다.
interface Cylinder {
radius: number;
height: number;
}
const Cylinder = (radius: number, height: number) => ({radius, height});
타입 공간, 값 공간이 같아 오류가 발생하는 케이스
interface Cylinder {
radius: number;
height: number;
}
const Cylinder = (radius: number, height: number) => ({radius, height});
function calculateVolume(shape: unknown) {
if (shape instanceof Cylinder) {
shape.radius
// ~~~~~~ Property 'radius' does not exist on type '{}'
}
}
- instanceof는 1. 런타임 연산자이고 2. 값에 대해 연산을 한다
- 때문에 instanceof Cylinder는 타입이 아닌 함수를 참조한다.
- 타입 코드명과 값 코드명이 같을 경우 혼란스러워지는 이유이다.
타입 공간 값 공간에 대한 개념
- 타입스크립트 코드를 읽을 때 타입인지 값인지 구분하는 방법을 터득하는 것이 중요하다.
type T1 = 'string literal';
type T2 = 123;
const v1 = 'string literal';
const v2 = 123;
type, interface
다음에 나오는 심벌은타입
인 반면const, let
선언에 쓰이는 것은값
이다.-
타입스크립트 플레이그라운드에서 타입스크립트로 구문을 작성하면 변환된 자바스크립트 코드를 보여준다.
JavaScript With Syntax For Types.
- 예제의 자바스크립트 코드
const v1 = 'string literal'; const v2 = 123;
자바스크립트 코드처럼 모든 값은 타입을 가지지만, 타입은 값을 가지지 않는다. type, interface같은 키워드는 타입 공간에만 존재한다는 것을 기억하자.
타입, 값 번갈아서 선언하기
- 타입 선언(:) 또는 단언문(as) 다음에 나오는 심벌은 타입이지만, = 다음에 오는 모든 것은 값이다.
interface Person {
first: string;
last: string;
}
const p: Person = { first: 'Jane', last: 'Jacobs' };
// - --------------------------------- Values
// ------ Type
function email(p: Person, subject: string, body: string): Response {
// ----- - ------- ---- Values
// ------ ------ ------ -------- Types
// COMPRESS
return new Response();
// END
}
class & enum
- 상황에 따라 타입과 값 두 가지 모두 사용한 예약어이다.
class Cylinder {
radius=1;
height=1;
}
function calculateVolume(shape: unknown) {
if (shape instanceof Cylinder) {
shape // OK, type is Cylinder
shape.radius // OK, type is number
}
}
- 클래스가 타입으로 쓰일 때 : 형태(속성, 메서드) 사용
- 클래스가 값으로 쓰일 때 : 생성자 사용
typeof 연산자
- typeof 연산자는 자바스크립트에도 존재하지만, 타입스크립트 타입이나 인터페이스 문법에 확장하여 사용할 수 있다.
- typeof는 값을 읽어서 타입스크립트 타입을 반환한다.
- 타입 공간 : typeof는 보다 큰 타입의 일부분으로 사용하거나 type 구문으로 이름을 붙이는 용도로도 사용할 수 있다.
- 값 공간 : typeof는 자바스크립트 런타임 타입을 가리키는 문자열을 반환한다.
- string, number, boolean, undefined, object, function
interface Person {
first: string;
last: string;
}
const p: Person = { first: 'Jane', last: 'Jacobs' };
// - --------------------------------- Values
// ------ Type
function email(p: Person, subject: string, body: string): Response {
// ----- - ------- ---- Values
// ------ ------ ------ -------- Types
// COMPRESS
return new Response();
// END
}
class Cylinder {
radius=1;
height=1;
}
function calculateVolume(shape: unknown) {
if (shape instanceof Cylinder) {
shape // OK, type is Cylinder
shape.radius // OK, type is number
}
}
type T1 = typeof p; // Type is Person
type T2 = typeof email;
// Type is (p: Person, subject: string, body: string) => Response
const v1 = typeof p; // Value is "object"
const v2 = typeof email; // Value is "function"
const v = typeof Cylinder; // 클래스가 실제 함수로 구현되기 때문에 값이 function
type T = typeof Cylinder; // 타입이 typeof Cylinder
- Cylinder 클래스는 생성자 함수이다.
declare let fn: T;
const c = new fn(); // 타입이 Cylinder
- InstanceType
을 사용해 생성자 타입을 인스턴스 타입으로 전환하는 것도 가능하다.
type C = InstanceType<typeof Cylinder>; // 타입이 Cylinder
두 공간 사이에서 다른 의미를 가지는 코드 패턴
- 값으로 쓰이는 this는 자바스크립트의 this 키워드이다.
- 타입으로 쓰이는 this는 타입스크립트의 타입(또는 다형성 this 라고 불린다.)
- 서브클래스의 메서드 체인을 구현할 때 유용하다.
-
값에서 &와 는 AND와 OR 비트 연산자이다. - 타입에서는 인터섹션과 유니온이다.
- const는 새 변수를 선언하지만, as const는 리터럴 또는 리터럴 표현식의 추론된 타입을 바꾼다.
- extends는 서브클래스(class A extends B) 또는 서브타입(interface A extends B)또는 제너릭 타입의 한정자(Generic
)를 정의할 수 있다. - in은 루프 또는 매핑된 타입에 등장한다.
- 예시로 든 this, typeof 외에도 다른 많은 연산자들과 키워드들은 타입 공간과 값 공간에서 다른 목적으로 사용될 수 있다.
만약 타입스크립트 코드가 잘 작동하지 않는다면 타입 공간과 값 공간을 혼동해서 잘못 작성했을 가능성이 크다.
- 함수를 구조 분해 할당을 이용하도록 변경한 코드이다.
- 자바스크립트에서는 정상적으로 작동하지만, 타입스크립트에서는 오류가 발생한다.
자바스크립트일 경우 )
interface Person {
first: string;
last: string;
}
// 이렇게 단일 객체 매개변수를 받도록 작성한 코드를
function email(options: {person: Person, subject: string, body: string}) {
// ...
}
// 구조분해할당으로 이렇게 변경할 수 있다.
function email({person, subject, body}) {
// ...
}
타입스크립트일 경우 )
interface Person {
first: string;
last: string;
}
function email({
person: Person,
// ~~~~~~ 바인딩 요소 'Person'에 암시적으로 'any' 타입 형식이 있습니다.
subject: string,
// ~~~~~~ 'string' 식별자 중복
// 'string'에 암시적으로 'any' 형식이 있음.
body: string}
// ~~~~~~ 'string' 식별자 중복
// 'string'에 암시적으로 'any' 형식이 있음.
) { /* ... */ }
- 값 관점에서 Person과 string이 해석되어, 오류가 발생한 것이다.
- 타입스크립트는 Person과 string이라는 변수를 생성하려고 했다.
- 문제를 해결하기 위해서는 타입과 값을 구분하는 작업이 필요하다.
function email(
{person, subject, body}: {person: Person, subject: string, body: string}
) {
// ...
}
출처
- 이펙티브 타입스크립트