2장. 타입
2.1 타입이란
1. 자료형으로서의 타입
자바스크립트는 7가지 데이터 타입 또는 자료형 타입을 사용해서 값의 종류를 명시할 수 있고, 메모리를 더욱 효율적으로 사용할 수 있다.
2. 집합으로서의 타입
타입은 값이 가질 수 있는 유효한 범위의 집합 -> 런타임에서 발생할 수 있는 유효하지 않은 값에 대한 에러 방지
3. 정적 타입과 동적 타입
| 타입을 결정하는 시점에 따라 분류
정적 타입 - 컴파일타임에 결정, ex. C, 자바, 타입스크립트 등
동적 타입 - 런타임에 결정, ex. 파이썬, 자바스크립트 등
| 컴파일타임: 기계가 소스코드를 이해할 수 있도록 기계어로 변환되는 시점
런타임: 이후 변환된 파일이 메모리에 적재되어 실행되는 시점
4. 강타입과 약타입
| 암묵적 타입 변환 여부에 따라 분류
암묵적 타입 변환이란 개발자가 의도적으로 타입을 명시하거나 바꾸지 않았는데도 컴파일러 또는 엔진 등에 의해 런타임에 타입이 자동으로 변경되는 것을 말한다.
자바스크립트는 약타입 언어(암묵적 타입 변환이 일어나는)이기 때문에 런타임에서 발생할 수 있는 에러를 예측하고 방지하는 코드를 작성하는 것이 프로그램을 안전하게 만드는 데 도움이 된다.
| 참고로 파이썬은 동적 타입, 강타입 언어이다.
5. 컴파일 방식
일반적인 컴파일은 사람이 이해할 수 있는 방식으로 작성한 코드를 컴퓨터가 이해할 수 있는 기계어로 바꿔주는 과정, 고수준 언어로 작성한 소스코드를 바이너리 코드로 변환
타입스크립트의 탄생 이유는 사람이 이해하기 쉬운 방식으로 코드를 작성하기 위해서가 아니라, 자바스크립트의 컴파일타임에 런타임 에러를 사전에 잡아내기 위한 것
따라서 자바스크립트에 타입이라는 레이어를 끼얹은 일종의 템플릿 언어 또는 확장 언어로 해석하는 의견도 있다.
2.2 타입스크립트의 타입 시스템
1. 타입 애너테이션 방식
변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언해서 어떤 타입 값이 저장될 것인지를 컴파일러에 직접 알려주는 문법
타입스크립트는 변수 이름 뒤에 : type
구문
2. 구조적 타이핑
| 명목적으로 구체화한 타입 시스템(nominal reified type system)과 구조적 타이핑(Structural type system)
명목적으로 구체화한 타입 시스템
- 타입을 사용하는 프로그래밍 언어에서 값이나 객체는 하나의 구체적인 타입을 가지고 있다.
- 타입은 이름으로 구분되며, 컴파일 이후에도 남아있다.
- 서로 다른 클래스끼리 명확한 상속 관계나 공통으로 가지고 있는 인터페이스가 없다면 타입은 서로 호환되지 않는다.
“이름”으로 타입을 구분하는 명목적인 타입 언어와는 달리 타입스크립트는 “구조”로 타입을 구분한다.
3. 구조적 서브타이핑
타입스크립트의 타입은 값의 집합이다. 타입스크립트의 타입 시스템을 지탱하고 있는 개념이 바로 구조적 서브타이핑(Structural Subtyping)이다.
구조적 서브타이핑은 객체가 가지고 있는 속성(프로퍼티)를 바탕으로 타입을 구분하는 것을 말한다.
따라서 이름이 다른 객체라도 가진 “속성이 동일”하다면, 서로 “호환이 가능”한 동일한 타입으로 여긴다.
// name 속성을 가진 Pet 타입으로 선언한 pet에 cat을 할당할 수 있다.
interface Pet {
name: string
}
interface Cat {
name: string
age: number
}
let pet: Pet;
let cat: Cat = { name: "Zag", age: 2 };
// ✅ OK
pet = cat;
// cat객체가 Pet 인터페이스가 가지고 있는 name 속성을 가지고 있어 pet.name 속성에 접근할 수 있어 정상적으로 실행된다.
interface Pet {
name: string
}
let cat = { name: "Zag", age: 2 };
function greet(pet: Pet) {
console.log(`Hello, ${pet.name}`);
}
greet(cat); // ✅ OK
4. 자바스크립트를 닮은 타입스크립트
명목적 타이핑은 타입의 동일성을 확인하는 과정에서 더 안전하다. 그런데도 타입스크립트가 구조적 타이핑을 채택한 이유는 타입스크립트가 자바스크립트를 모델링한 언어이기 때문이다.
자바스크립트는 덕 타이핑(duck typing)을 기반으로 한다.
| 덕 타이핑: 어떤 함수의 매개변숫값이 올바르게 주어진다면, 그 값이 어떻게 만들어졌는지 신경 쓰지 않고 사용한다는 개념
어떤 타입에 부합하는 변수와 메서드를 가질 경우, 해당 타입에 속하는 것으로 간주하는 방식
”만약 어떤 새가 오리처럼 걷고, 헤어치며 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다”
둘 다 이름으로 구분하는게 아니라 “객체가 가진 속성(필드)”을 기반으로 타입을 검사하지만 ‘타입을 검사하는 시점’이 다른다. 덕 타이핑은 런타임, 구조적 타이핑은 컴파일타임
5. 구조적 타이핑의 결과
명목적 타이핑 언어의 특징을 가미한, 식별할 수 있는 유니온(dicriminated unions) 방법 탄생
6. 타입스크립트의 점진적 타입 확인
점진적 타입(graudally typed)검사란 컴파일 타임에 타입을 검사하면서, 필요에 따라 타입 선언 생략을 허용하는 방식이다.
any 타입으로 추론되는 것을 허락하지 않도록 tsconfig의 noImplicitAny
옵션을 true
로 설정하는 게 좋다.
7. 자바스크립트 슈퍼셋으로서의 타입스크립트
8. 값 vs. 타입
값과 타입은 타입스크립트에서 별도의 네임스페이스에 존재한다. (값 공간과 타입 공간이 별도로 존재한다.)
값과 타입 공간에 동시에 존재하는 심볼도 있으며, 대표적인 것이 class
와 enum
이다. 런타임에서 실제 값으로도 사용될 수 있다.
9. 타입을 확인하는 방법
typeof
값 공간에서 typeof 클래스명
값은 function
타입 공간에서 typeof 클래스명
의 반환 값은 인스턴스의 타입이 아니라 new 키워드를 사용할 때 볼 수 있는 생성자 함수이다.
// 2.2.9
instanceof
타입 단언 as
타입 가드
2.3 원시 타입
- 자스에서 값은 타입을 가지지만 변수는 타입을 가지지 않는다.
- 타스는 이 변수에 타입을 지정하는 타입 시스템 체계를 구축한다. 자바스크립트 원시 값은 타스에서 원시 타입으로 존재
원시 값과 원시 래퍼 객체
JS는 내장 타입을 파스칼 표기법, TS는 대응하는 타입을 소문자로 표기
- 자바스크립트에서 타입을 파스칼 표기법으로 표기하면 이것을 ‘원시 래퍼 객체’라고 부른다. (null, undefined를 제외한 모든 원시 값은 해당 원시 값을 래핑한 객체를 가진다.)
- 타입스크립트에도 원시 래퍼 객체가 존재하는데, 둘은 엄연히 다르다는 것을 주의해야한다.
1. boolean
true, false
2. undefined
초기화되어 있지 않거나 존재하지 않음(옵셔널 연산자)
3. null
명시적/의도적으로 값이 아직 비어있을 수 있음을 나타냄
(null === undefined // true 이지만 잘 구분해서 사용하자)
4. number
숫자에 해당하는 모든 원시 값 (정수, 부동소수점수, NaN, Infinity 등)
5. bigInt
ES2020, TS v3.2부터 사용 가능
Number.MAX_SAFE_INTEGER // (2^53)-1
를 넘어가는 값을 처리할 수 있다.
6. string
문자열 (’’, “”, ``(템플릿 리터럴))
7. symbol
ES2015, Symbol()함수를 이용하면 어떤 값과도 중복되지 않는 유일한 값을 생성한다.
TS에는 symbol 타입과 const 선언에서만 사용할 수 있는 unique symbol 타입
이라는 symbol 하위 타입도 있다.
null이나 undefined는 tsconfig 옵션이나 사용자 취향에 따라 다르게 사용될 여지가 있다.
- strictNullChecks 옵션: 사용자가 명시적으로 해당 타입에 null, undefined를 포함해야만 이를 사용할 수 있는 옵션
- null, undefined가 될 수 있는 경우에 발생하는 에러는 타입 가드(선호)나 단언문(! 연산자)을 이용해 걸러낸다.
2.4 객체 타입
앞서 언급한 7가지 원시 타입에 속하지 않는 모든 값 (객체, 배열, 함수, 정규식 등)
다양한 형태의 객체를 배열, 클래스 타입으로 지정할 수도 있고 원하는 타입으로 만들어 사용한다.
1. object
object 타입은 가급적 사용하지 말도록 권장된다. any 타입과 유사하게(any는 원시 타입도 포함한다) 객체에 해당하는 모든 타입 값을 유동적으로 할당할 수 있어 정적 타이핑의 의미가 크게 퇴색되기 때문이다.
→ 실무에서 잘 사용하지 않는다.
2.
JS에서 객체 리터럴 방식으로 객체를 생성할 때 사용한다. TS에서는 객체를 타이핑할 때 중괄호 안에 객체의 속성 타입을 지정해주는 식으로 사용한다. 타이핑되는 객체의 구조와 일치해야 한다.
빈 객체 타입을 지정하기 위해서는 보다 유틸리티 타입으로 Record<string, never>
사용하는게 좋다.
3. array
TS의 배열 타입은 하나의 타입 값만 가질 수 있다(엄격하다). JS와 마찬가지로 원소 개수는 영향을 주지 않는다
Array 키워드나 대괄호([])를 사용해서 선언
튜플 타입도 대괄호로 선언한다는 점을 주의하자 TS 튜플 타입은 배열과 유사하지만, 대괄호 내부에는 선언 시점에 지정해준 타입 값만 할당할 수 있다. 즉 원소 개수도 정해지는 것 (객체 리터럴처럼 선언하지 않은 속성을 할당하거나, 선언한 속성을 할당하지 않을때와 비슷)
4.type과 interface 키워드
객체를 타이핑하기 위해 사용하는 키워드
type vs. interface
...
5. function
JS의 function 타입을 그대로 사용하지 않고,호출 시그니처 정의 방식을 사용한다.
| 호출 시그니처: TS에서 함수 타입을 정의할 때 사용하는 문법, 함수의 매개변수과 반환 값의 타입을 (화살표 함수 방식으로) 명시하는 역할