[2장] 타입
2.1 타입이란
- 자료형으로서의 타입
- 최신 ECMAScript 표준을 따르는 자바스크립트는 다음과 같은 7가지 데이터 타입을 정의한다.
- undefined
- null
- Boolean
- String
- Symbol
- Numeric(Number와 BigInt)
- Object
- 최신 ECMAScript 표준을 따르는 자바스크립트는 다음과 같은 7가지 데이터 타입을 정의한다.
- 집합으로서의 타입
- 타입은 값이 가질 수 있는 유효한 범위의 집합을 말한다.
- number 타입에는 1, 2, 5, 74, 124 등이 들어가고, string 타입에는 ‘abc’, ‘안녕’ 등이 들어간다.
- 정적 타입과 동적 타입
- 정적 타입 시스템에서는 모든 변수의 타입이 컴파일 타임에 결정된다.
- 동적 타입 시스템에서는 변수 타입이 런타임에서 결정된다.
- 개발자가 작성한 소스코드를 실행하려면 몇 가지 과정을 거쳐야 하는데 시점에 따라 컴파일타임과 런타임으로 구분할 수 있다. 기계(컴퓨터, 엔진)가 소스코드를 이해할 수 있도록 기계어로 변환되는 시점을 컴파일타임이라고 하며 이후 변환된 파일이 메모리에 적재되어 실행되는 시점을 런타임이라고 부른다.
- 강타입과 약타입
- 개발자가 의도적으로 타입을 명시하거나 바꾸지 않았는데도 컴파일러 또는 엔진 등에 의해서 런타임에 타입이 자동으로 변경되는 것을 암묵적 타입 변환(Implicit coercion/conversion)이라고 한다.
- 암묵적 타입 변환 여부에 따라 타입 시스템을 강타입(strongly type)과 약타입(weakly type)으로 분류할 수 있다.
- 컴파일 방식
- 타입스크립트의 컴파일 결과물은 여전히 사람이 이해할 수 있는 방식인 자바스크립트 파일이다.
2.2 타입스크립트의 타입 시스템
타입 애너테이션 (type annotation)
- 타입 애너테이션이란 변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언해서 어떤 타입 값이 저장될 것인지를 컴파일러에 직접 알려주는 문법이다.
구조적 타이핑 (Structural type system)
- 이름으로 타입을 구분하는 명목적인 타입 언어의 특징과 달리 타입스크립트는 구조로 타입을 구분한다. 이것을 구조적 타이핑이라고 한다.
구조적 서브타이핑
- 타입스크립트의 타입은 값의 집합으로 생각할 수 있다.
- 타입은 단지 집합에 포함되는 값이고 특정 값은 많은 집합에 포함될 수 있다.
interface Pet {
name: string;
}
const cat = { name: 'sfsd', age: 2 };
function greet(pet: Pet) {
console.log(pet.name);
}
greet(cat);
- 위 코드는 cat이 age라는 프로퍼티를 가지고 있어 greet 함수를 실행할 때 에러가 발생할 것 같지만 에러가 발생하지 않는다.
자바스크립트와 닮은 타입스크립트
- 자바스크립트는 본질적으로 덕 타이핑(duck typing)을 기반으로 한다. 덕 타이핑은 어떤 함수의 매개변숫값이 올바르게 주어진다면 그 값이 어떻게 만들어졌는지 신경 쓰지 않고 사용한다는 개념이다.
'만약 어떤 새가 오리처럼 걷고, 헤엄치며 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.';
- 타입스크립트는 이런 동작으로 그대로 모델링하여, 명시적인 이름을 가지고 타입을 구분하는 대신 객체나 함수가 가진 구조적 특징을 기반으로 타이핑하는 방식을 택했다.
- 쉬운 사용성과 안정성이라는 두 가지 목표 사이의 균형을 중시하는 타입스크립트에서는 객체 간 속성이 동일하다면 서로 호환되는 구조적 타입 시스템을 제공하여 더욱 편리성을 높였다.
- 그러나 덕 타이핑은 런타임에 타입을 검사한다. 자바스크립트는 덕 타이핑 언어다. 구조적 타이핑은 컴파일타임에 타입체커가 타입을 검사한다.
타입스크립트의 점진적 타입 확인
- 타입스크립트는 점진적으로 타입을 확인하는 언어다.
- 타입을 지정한 변수와 표현식은 정적으로 타입을 검사하지만 타입 선언이 생략되면 동적으로 검사를 수행한다.
- 타입 선언을 생략하면 암시적 타입 변환이 일어난다.
function add(x, y) {
return x + y;
}
// 위 코드는 아래와 같이 암시적 타입 변환이 일어난다.
function add(x: any, y: any): any;
// any 타입으로 추론한다.
자바스크립트 슈퍼셋으로서의 타입스크립트
- 모든 자바스크립트 코드는 타입스크립트라고 볼 수 있지만 반대로 모든 타입스크립트 코드가 자바스크립트 코드인 것은 아니다.
값 vs 타입
- 값은 프로그램이 처리하기 위해 메모리에 저장하는 모든 데이터다.
- 다르게 말하면 프로그램에서 조작하고 다룰 수 있는 어떤 표현이며 다양한 형태의 데이터를 포함한다.
- 프로그래밍 관점에서는 문자열, 숫자, 변수, 매개변수 등이 값에 해당한다.
- 타입은 type이나 interface 등
- 타입스크립트 문법인 type으로 선언한 내용은 자바스크립트 런타임에서 제거되기 때문에 값 공간과 타입 공간은 서로 충돌하지 않는다.
- 타입은 주로 타입 선언(:) 또는 단언 문(as)로 작성하고 값은 할당 연산자인 =으로 작성한다.
- 타입스크립트에는 값과 타입 공간에 동시에 존재하는 것도 있다. → 대표적인 것이 클래스와 enum이다.
- 클래스는 값과 타입 공간 모두에 포함될 수 있다.
- enum 또한 타입 공간 값 공간 모두에서 사용될 수 있다.
타입을 확인하는 방법
- 타입스크립트에서 typeof 연산자는 값에서 쓰일 때와 타입에서 쓰일 때의 역할이 다르다.
- 예를 들어, object와 function의 경우 값에서 사용하면 object와 function이 되지만, 타입에서 사용하면 값을 읽고 타입스크립트 타입을 반환한다.
class Developer {
name: string;
sleepingTime: number;
constructor(name: string, sleepingTime: number) {
this.name = name;
this.sleepingTime = sleepingTime;
}
}
const d = typeof Developer; // 값이 function
type T = typeof Developer; // 타입이 typeof Developer
- Developer라는 class가 존재할 때, 자바스크립트의 클래스는 결국 함수이기 때문에 값 공간에서 typeof Developer의 값은 function이 된다. 타입 공간에서 typeof Developer의 반환 값은 조금 특이한데 type T에 할당된 Developer는 인스턴스의 타입이 아니라 new 키워드를 사용할 때 볼 수 있는 생성자 함수이기 때문이다.
- 타입 단언(as)은 개발자가 해당 값의 타입을 더 잘 파악할 수 있을 때 사용되며 강제 형 변환과 유사한 기능을 제공한다.
2.3 원시 타입
- 자바스크립트의 7가지 원시 값은 타입스크립트에서 원시 타입으로 존재한다.
- 원시 값과 원시 래퍼 객체
- 앞서 자바스크립트의 내장 타입을 파스칼 표기법으로 표기했다. 반면 타입스크립트에서는 이에 대응하는 타입을 소문자로 표기한다. 자바스크립트는 컴파일 시점에 타입스크립트의 타입 시스템이 적용되지 않으므로 타입스크립트와 구별하기 위해 소문자로 표기하지 않았다. 타입을 파스칼 표기법으로 표기하면 자바스크립트에서 이것을 원시 래퍼 객체라고 부른다. null과 undefined를 제외한 모든 원시 값은 해당 원시 값을 래핑한 객체를 가진다.원시 래퍼 객체는 이름에서 알 수 있듯이 원시 값이 아닌 객체라는 점에 주의하자. 따라서 타입스크립트에서는 내장 원시 타입에 해당하는 타입을 파스칼 표기법으로 쓰지 않도록 주의해야 한다. 타입스크립트에도 원시 래퍼 객체가 존재하는데 이것은 고유한 타입으로 분류되기 때문에 둘은 엄연히 다르다.
boolean
- true / false
- 자바스크립트에는 boolean 원시 값은 아니지만 형 변환을 통해 true / false로 취급되는 Truthy / Falsy 값이 존재한다. 앞서 말했듯이 이 값은 boolean 원시 값이 아니므로 타입스크립트에서도 boolean 타입에 해당하지 않는다.
undefined
- 쓰레기값
null
- null은 명시적, 의도적으로 값이 아직 비어있을 수 있음을 보여준다.
number
- 자바스크립트에서 숫자에 해당하는 원시 값 중 NaN이나 Infinity도 포함된다.
bigInt
- 자바스크립트에서는 가장 큰 수인 Number.MAX_SAFE_INTEGER(2^53-1)를 넘어가는 값을 처리할 수 없었는데 bigInt를 사용하면 이보다 큰 수를 처리할 수 있다. number 타입과 bigint 타입은 엄연히 서로 다른 타입이기 때문에 상호 작용은 불가능하다.
string
symbol
- ES2015에서 도입된 데이터 타입으로
Symbol()
함수를 사용하면 어떤 값과도 중복되지 않는 유일한 값을 생성할 수 있다. - 타입스크립트에는 symbol 타입과 const 선언에서만 사용할 수 있는 unique symbol 타입이라는 symbol의 하위 타입도 있다.
2.4 객체 타입
object
- object 타입은 가급적 사용하지 말도록 권장되는데 나중에 다룰 any 타입과 유사하게 객체에 해당하는 모든 타입 값을 유동적으로 할당할 수 있어 정적 타이핑의 의미가 크게 퇴색되기 때문이다.
{}
- ex.
{ title: string; description: string }
array
number[]
/Array<number>
- 튜플도 대괄호로 선언한다.
type과 interface
- 객체를 타이핑하기 위해 자주 사용하는 키워드
- 중복적인 요소를 처리할 수 있다.
- 타입스크립트에서는 일반적으로 변수 타입을 명시적으로 선언하지 않아도 컴파일러가 자동으로 타입을 추론한다.
function
- 자바스크립트에서는 함수도 일종의 객체로 간주하지만 typeof 연산자로 함수 타입을 출력해보면 함수를 function이라는 별도 타입으로 분류한다는 것을 확인할 수 있다.
- 호출 시그니처
- 타입스크립트에서 함수 타입을 정의할 때 사용하는 문법이다. 함수 타입은 해당 함수가 받는 매개변수와 반환하는 값의 타입으로 결정된다. 호출 시그니처는 이러한 함수의 매개변수와 반환 값의 타입을 명시하는 역할을 한다.
type add = (a: number, b: number) => number;