티스토리 뷰

2.1 타입이란


2.1.1 자료형으로서의 타입

  • 프로그래밍 언어에서 변수란 값을 저장할 수 있는 공간(컴퓨터의 메모리)이자 을 가리키는 상징적인 이름이다.
  • 변수에 저장할 수 있는 값의 종류는 프로그래밍 언어마다 다르다.
  • 자바스크립트는 다음과 같은 7가지 데이터 타입(자료형)을 정의한다.
    • undefined
    • null
    • Boolean
    • String
    • Symbol
    • Numeric (Number 와 BigInt)
    • Object

2.1.2 집합으로서의 타입

  • 타입은 값이 가질 수 있는 유효한 범위의 집합을 말한다.
  • 타입 시스템은 코드에서 사용되는 유효한 값의 범위를 제한해서 런타임에서 발생할 수 있는 유효하지 않은 값에 대한 에러를 방지해 준다.

func() 함수의 인자로 들어갈 수 있는 값을 number 타입으로 제한

2.1.3 정적 타입과 동적 타입

  • 만약 자바스크립트만 사용했다면 변수와 값을 다룰 때 타입은 고려하지 않고 코드를 작성했을 수 있다.
    • 그러나, 자바스크립트에도 분명히 타입은 존재한다!
    • 다만, 개발자가 컴파일 이전에 타입을 직접 정의해 줄 필요가 없었을 뿐이다!
  • 타입을 결정하는 시점에 따라 타입을 정적 타입(static type) 동적 타입(dynamic type)으로 분류할 수 있다.
    • 정적 타입 : 모든 변수의 타입이 컴파일 타임에 결정. (ex. C, 자바, 타입스크립트)
      • 컴파일 타임에 타입 에러를 발견할 수 있기에 프로그램의 안정성 보장 가능.
    • 동적 타입 : 변수 타입이 런타임에 결정. (ex. 파이썬, 자바스크립트)
      • 프로그램을 실행할 때 타입 에러가 발견되기에 개발 과정에서는 자유롭게 코드를 작성할 수 있지만, 언제 프로그램에 오류가 생길지 모르는 불안감.

2.1.4 강타입과 약타입

  • 암묵적 타입 변환(implicit conversion) : 개발자가 의도적으로 타입을 명시하거나 바꾸지 않았는데도 컴파일러 또는 엔진 등에 의해서 런타임에 타입이 자동으로 변경되는 것
  • 암묵적 타입 변환 여부에 따라 타입 시스템을 강타입(strongly type)약타입(weekly type) 으로 분류할 수 있다.
    • 강타입 : 서로 다른 타입을 갖는 값끼리 연산 시, 컴파일러 또는 인터프리터에서 에러 발생.
    • 약타입 : 서로 다른 타입을 갖는 값끼리 연산 시, 컴파일러 또는 인터프리터가 내부적으로 판단해서 특정 값의 타입을 변환하여 연산을 수행한 후 값을 도출.
  • 암묵적 타입 변환은 개발자가 명시적으로 타입을 변환하지 않아도 다른 데이터 타입끼리 연산을 진행할 수 있는 편리함을 제공하지만, 작성자의 의도와 다르게 동작할 수 있기에 예기치 못한 오류가 발생할 가능성도 높아진다.

2.1.5 컴파일 방식

  • 컴파일의 일반적인 의미는 사람이 이해할 수 있는 방식으로 작성한 코드를 컴퓨터가 이해할 수 있는 기계어로 바꿔주는 과정을 말한다.
  • 그러나, 타입스크립의 컴파일 결과물은 여전히 사람이 이해할 수 있는 자바스크립트 파일이다.
    (다른 타입의 개념을 사용하는 언어와는 구별되는 특징을 가지고 있다.)
  • 타입스크립트가 탄생한 이유는 자바스크립트의 컴파일 타임에 런타임 에러를 사전에 잡아내기 위한 것이다.

 

2.2 타입스크립트의 타입 시스템


2.2.1 타입 애너테이션 방식

  • 타입 애너테이션(type annotation) 이란, 변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언해서 어떤 타입 값이 저장될 것인지를 컴파일에 직접 알려주는 문법이다.

데이터 타입 명시

2.2.2 구조적 타이핑

  • 타입을 사용하는 여러 프로그래밍 언어에서 값이나 객체는 하나의 구체적인 타입을 가지고 있다.
    • 명목적으로 구체화한 타입 시스템이라 부른다. (nominal reified type systems)
  • 서로 다른 클래스끼리 명확한 상속 관계나 공통으로 가지고 있는 인터페이스가 없다면 타입은 서로 호환되지 않는다.
  • 그러나, 타입스크립트는 이름으로 타입을 구분하는 명목적인 타입 언어의 특징과 달리 구조로 타입을 구분한다.
    • 구조적 타이핑(structural type system) 이라고 한다.

2.2.3 구조적 서브 타이핑

  • 객체가 가지고 있는 속성(프로퍼티)을 바탕으로 타입을 구분하는 것이다.
    • 이름이 다른 객체라도 가진 속성이 동일하다면 타입스크립트 서로 호환이 가능한 동일한 타입으로 여긴다.

cat 객체는 name 속성에 접근 가능하므로 정상 실행 (구조적 타이핑)

  • 타입스크립트의 서브 타이핑, 즉 타입의 상속 역시 구조적 타이핑을 기반으로 하고 있다.

Developer 는 Person 이 갖고 있는 속성을 가지고 있기에 정상 동작

  • 서로 다른 두 타입 간의 호환성은 오로지 타입 내부의 구조에 의해 결정된다.

2.2.4 자바스크립트를 닮은 타입스크립트

  • 명목적 타이핑의 경우, 이름으로 타입을 구분하기에 구조가 같더라도 이름이 다르다면 다른 타입으로 취급한다.
  • 자바스크립트는 본질적으로 덕 타이핑을 기반으로 한다.
    • 덕 타이핑 : 어떤 함수의 매개변숫값이 올바르게 주어진다면 그 값이 어떻게 만들어졌는지 신경 쓰지 않고 사용한다는 개념.
      • "만약 어떤 새가 오리처럼 걷고, 헤엄치며 꽥꽥 거리는 소리를 낸다면 나는 오리라고 부를 것이다."
      • 즉, 객체가 어떻게 만들어졌는 지 상관없이 원하는 동작을 하게 하면 신경쓰지 않는다는 것을 의미
  • 타입스크립트는 이런 동작을 모델링하여 명시적인 이름을 가지고 타입을 구분하는 대신 객체나 함수가 가진 구조적 특징을 기반으로 타이핑하는 방식을 택했다.
  • 덕 타이핑은 런타임에 타입을 검사하고 구조적 타이핑은 컴파일 타임에 타입 체커가 타입을 검사한다.

2.2.5 값 vs 타입

  • 타입스크립트는 타입이라는 개념이 등장하며 :type 형태로 타입을 명시한다.
  • 혹은 type이나 interface 키워드로 커스텀 타입을 정의할 수도 있다.
  • 값과 타입의 공간의 이름은 서로 충돌하지 않기에 타입과 변수를 같은 이름으로 정의할 수 있는데 타입스크립트가 자바스크립트의 슈퍼셋인 것과 관련이 있다.
    • 타입 스크립트 문법인 type으로 선언한 내용은 자바스크립트 런타임에서 제거되기에 값과 타입의 공간은 서로 충돌하지 않는다.

타입스크립트 코드

 

자바스크립트 컴파일 결과

  • 값과 타입 공간에 동시에 존재하는 심볼도 있다.
    • 대표적으로 클래스와 enum이다.
  • 타입스크립트에서 클래스는 값과 타입 공간 모두에 포함될 수 있다.
    • 클래스는 타입 애너테이션으로 사용할 수 있지만 런타임에서 객체로 변환되어 자바스크립트의 값으로 사용

값과 타입 공간 모두 포함되는 클래스

  • enum 역시, 런타임에 객체로 변환되는 값이다. 
    • 클래스처럼 타입 공간에서 타입을 제한하는 역할을 하지만 자바스크립트 런타임에서 실제로 값으로도 사용될 수 있다.

타입스크립트 코드
자바스크립트 컴파일 결과

더보기

1. TypeScript enum 선언

 
enum fruit {
  Apple,
  Banana,
  Orange
}

이는 내부적으로 Apple = 0, Banana = 1, Orange = 2와 같은 숫자 열거형(Number Enum)으로 동작

2. JavaScript 컴파일 결과 분석

TypeScript는 위의 enum을 아래와 같이 컴파일합니다:

 
var fruit;
(function (fruit) {
    fruit[fruit["Apple"] = 0] = "Apple";
    fruit[fruit["Banana"] = 1] = "Banana";
    fruit[fruit["Orange"] = 2] = "Orange";
})(fruit || (fruit = {}));

① 즉시 실행 함수 (IIFE, Immediately Invoked Function Expression)

(function (fruit) { ... })(fruit || (fruit = {}));
  • fruit이라는 변수를 undefined가 아닌 객체로 초기화하기 위해 IIFE를 사용
  • fruit || (fruit = {})는 fruit이 이미 정의되어 있다면 그대로 사용하고, 없으면 빈 객체 {}로 초기화

② 숫자 → 문자열 매핑

fruit[fruit["Apple"] = 0] = "Apple";
  1. fruit["Apple"] = 0 → fruit 객체에 "Apple"이라는 키로 0을 저장
  2. fruit[0] = "Apple" → 숫자 0을 키로 "Apple"을 저장

즉, 이 과정을 통해 fruit 객체는 아래처럼 구성

 
{
  "Apple": 0,
  "Banana": 1,
  "Orange": 2,
  0: "Apple",
  1: "Banana",
  2: "Orange"
}

 

이렇게 하면 양방향 매핑이 가능

 
console.log(fruit.Apple); // 0
console.log(fruit[0]); // "Apple"

 

즉, fruit.Apple을 통해 0을 얻을 수도 있고, fruit[0]을 통해 "Apple"을 얻을 수도 있는 구조

2.2.6 타입을 확인하는 방법

  • 타입스크립트에서 typeof, instanceof 그리고 타입 단언을 사용해 타입을 확인할 수 있다.
    • typeof : 연산하기 전에 피연산자의 데이터 타입을 나타내는 문자열 반환
      • typeof 연산자도 값에서 쓰일 때와 타입에서 쓰일 때의 역할이 다르다.
     

 

  • instanceof 연산자를 사용하면 프로토타입 체이닝 어딘가에 생성자의 프로토타입 속성이 존재하는지 판단할 수 있다.
  • as 키워드를 사용해 타입을 강제할 수도 있다.

 

2.3 원시 타입


2.3.1 boolean

  • 오직 true와 false 값만 할당할 수 있는 boolean 타입이다.

2.3.2 undefined

  • 값이 아직 할당/정의되지 않았다는 의미의 타입으로 undefined 값만 할당할 수 있다.
    • 초기화되어 있지 않거나 존재하지 않음을 나타낸다.

2.3.3 null

  • 보통 빈 값을 할당해야 할 때 null을 사용한다.
    • 명시적/의도적으로 값이 아직 비어있을 수 있음을 보여준다.

2.3.4 number

  • 자바스크립트의 숫자에 해당하는 모든 원시 값을 할당할 수 있다.
  • NaN (Not a number), Infinity 도 포함된다.

2.3.5 bigInt

  • ES2020에서 도입된 타입으로 타입스크립트 3.2 버전부터 사용 가능
  • Number.MAX_SAFE_INTEGER(2^53-1) 넘어가는 수를 처리할 수 있다.
  • number 타입과 엄연히 다른 타입이기에 상호 작용은 불가능하다.

2.3.6 string

  • 문자열을 할당할 수 있는 타입이다.
  • 공백, ', ", ` 으로 감싼 템플릿 리터럴 문법도 있다.

2.3.7 symbol

  • ES2015에서 도입된 타입으로 Symbol() 함수를 사용하면 어떤 값과도 중복되지 않는 유일한 값을 생성할 수 있게 된다.

 

2.4 객체 타입


  • 7가지 원시 타입에 속하지 않는 값은 모두 객체 타입으로 분류할 수 있다.

2.4.1 object

  • object 타입은 객체에 해당하는 모든 타입 값을 유동적으로 할당할 수 있어 정적 타이핑의 의미가 크게 퇴색되어 가급적 사용하지 말도록 권장된다.
  • 원시타입은 object 타입에 속하지 않는다.

4.2.2 {}

  • 타입스크립트에서는 객체를 타이핑할 때도 중괄호를 쓸 수 있으며, 중괄호 안에 객체의 속성 타입을 지정해 주는 식으로 사용한다.
  • {} 타입으로 지정된 객체에는 어떤 값도 속성으로 할당할 수 없다.
    • 사실, 빈 객체 타입을 지정하기 위해서는 유틸리티 타입으로 Record <string, never>처럼 사용하는 것이 바람직하다.

2.4.3 array

  • 타입스크립트에서는 배열을 array라는 별도 타입으로 다룬다.
  • Array 키워드로 선언하거나 대괄호[]를 사용해서 선언하는 방법이 있다.
  • 튜플은 대괄호 내부에는 선언 시점에 지정해 준 타입 값만 할당할 수 있으며, 원소 개수도 타입 선언 시점에 미리 정해진다.

2.4.4 type과 interface 키워드

  • 보통 object 타입을 쓰지 않고, 객체를 타이핑하기 위해 type, interface 키워드를 사용한다.

2.4.5 function

  • 함수를 별도 함수 타입으로 지정할 수 있다.
  • 유의해야 할 점
    • 자바스크립트에서 typeof 연산자로 확인한 function이라는 키워드 자체를 타입으로 사용하지 않는다.
    • 함수는 매개변수 목록을 받을 수 있는데 타입스크립트에서는 매개변수도 별도 타입으로 지정해야 한다.
    • 함수가 반환하는 값이 있다면 반환 값에 대한 타이핑도 필요하다.
     

 

  • 함수 타입을 정의할 때 호출 시그니처(call signature)를 사용한다.
    • 호출 시그니처는 함수의 매개변수와 반환 값의 타입을 명시하는 역할을 한다. 
      • type add = (a: number, b: number) => number;