[2장] 타입

woodstock·2024년 1월 5일
post-thumbnail

2.1 타입이란

자료형으로서의 타입

컴퓨터의 메모리 공간은 한정적이기 때문에 값의 크기를 명시하면 컴퓨터가 값을 효율적이고 안전하게 저장할 수 있다.

이를 위해 최신 ECMAScript 표준을 따르는 자바스크립트는 7가지 데이터타입을 정의한다.

  • undefined
  • null
  • Boolean
  • String
  • Symbol
  • Numeric (Number와 BigInt)
  • Object

데이터 타입은 컴파일러에 값의 형태를 알려주는 분류 체계로, 메모리 관점에서의 데이터 타입은 프로그래밍 언어에서 일반적으로 타입으로 부르는 개념과 같다.

집합으로서의 타입

프로그래밍에서의 타입은 수학의 집합과 유사하며, 값이 가질 수 있는 유효한 범위의 집합을 의미한다.

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

예제 1.

const num: number = 123;
const str: string = "abc";

function func(n: number) {
  // ...
}

func(str); // Argument of type 'string' is not assignable to parameter of type 'number'

예를 들어, func()이라는 함수의 인자로 number타입 값만 할당할 수 있도록 제한되어 있다면, number의 집합에 속하지 않는 string타입의 strfunc()함수의 인자로 사용할 때 에러가 발생한다.

이는 타입이 집합의 경계처럼 동작하면 func() 함수의 인자로 들어갈 수 있는 값을 number타입의 집합으로 제한하기 때문이다.

예제 2.

function double(n: number) {
  return n * 2;
}

double(2); // 4
double("z"); // 🚨 Error: Argument of type 'string' is not assignable to parameter of type 'number'.(2345)

double() 함수는 숫자를 인자로 받아 그 숫자를 두 배로 반환하는 함수이다.

만약 숫자가 아닌 다른 타입의 값을 인자로 전달하면, 의도치 않은 작업을 수행하여 원하는 값을 얻지 못할 수 있다.

하지만 함수의 매개변수 타입을 명시하면,타입스크립트 컴파일러는 함수를 호출할 때 호환되는 인자로 호출했는지를 판단한다.

예를 들어, double(2)number 타입의 인자를 전달하므로 문제 없이 컴파일되지만, double("z")string 타입의 인자를 전달하므로 에러가 발생한다.

이는 타입이 함수의 매개변수에 대한 값의 범위를 제한하기 때문이다.

정적 타입과 동적 타입

타입을 결정하는 시점에 따라 정적 타입과 동적 타입으로 분류할 수 있다.

  • 정적 타입 시스템 : 모든 변수의 타입이 컴파일 타임에 결정되며, 컴파일 타임에 타입 에러를 발견할 수 있어 프로그램의 안정성을 보장할 수 있다.
  • 동적 타입 시스템 : 변수 타입이 런타임에서 결정되며, 개발자가 직접 타입을 정의해 줄 필요가 없다. 하지만 프로그램을 실행할 때 에러가 발견될 수 있다.

강타입과 약타입

개발자가 의도적으로 타입을 명시하거나 바꾸지 않았는데도 컴파일러 또는 엔진 등에 의해서 런타임에 타입이 자동으로 변경되는 것을 암묵적 타입 변환이라고 한다.

암묵적 타입 변환 여부에 따라 타입 시스템을 강타입약타입으로 분류할 수 있다.

  • 강타입 : 서로 다른 타입을 갖는 값끼리 연산을 시도하면 컴파일러 또는 인프리터에서 에러가 발생한다.
  • 약타입 : 서로 다른 타입을 갖는 값끼리 연산할 때는 컴파일러 또는 인터프리터가 내부적으로 판단해서 특정 값의 타입을 변환하여 연산을 수행한 후 값을 도출한다.

예제

// 자바스크립트 - 약타입

console.log("2" - 1); // 1

예를 들어, 자바스크립트는 약타입 언어로, 타입이 명백하게 잘못 작성된 코드도 암묵적 타입 변환을 수행하여 결과를 도출하며, 이는 예기치 못한 오류를 발생시킬 가능성이 있으므로 타입 안정성을 유지하는 것이 중요하다.

// 타입스크립트 - 강타입

console.log("2" - 1); // "2" error
// type error
// The left-hand side of an arithmetic operation must be of type ‘any’, ‘number’, ‘bigint’ or an enum type.

타입 검사기가 프로그램에 타입을 할당하는 데 사용하는 규칙 집합을 타입 시스템이라고 하는데, 크게 두 가지로 구분한다.

  • 명시적 타입 시스템 : 개발자가 직접 타입을 명시해줘야 한다.
  • 자동 타입 추론 시스템 : 컴파일러가 자동으로 타입을 추론한다.

타입스크립트는 이 두 가지 방식을 모두 사용할 수 있고, 이를 통해 코드의 안정성과 가독성을 높일 수 있다.

컴파일 방식

타입스크립트는 자바스크립트의 타입 에러를 컴파일 타임에 미리 발견하기 위해 만들어졌기 때문에 컴파일 시 타입 정보가 없는 순수 자바스크립트 코드를 생성한다.

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

타입 애너테이션 방식

변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언해서 어떤 타입 값이 저장될 것인지를 컴파일러에게 직접 알려주는 문법이다.

타입스크립트의 타입 선언 방식은 아래와 같이 변수 이름 뒤에 : type 구문을 붙여주는 것이다.

let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let x: [string, number]; // tuple

구조적 타이핑

명목적으로 구체화한 타입 시스템에서는 값이나 객체의 타입은 이름으로 구분된다.

명목적으로 구체화한 타입 시스템
타입을 사용하는 여러 프로그래밍 언어에서 값이나 객체는 하나의 구체적인 타입을 가지고 있다. 타입은 이름으로 구분되며 컴파일타임 이후에도 남아있는데, 이것을 명목적으로 구체화한 타입 시스템이라고 부르기도 한다.

예제 1.

class Animal {
 String name;
 int age;
}

예를 들어, Animal 클래스는 nameage라는 속성을 가진 타입이다. 이 클래스는 그 자체의 이름, 즉 Animal에 의해 정의되고 구별된다.

또한, 서로 다른 클래스끼리 명확한 상속 관계나 공통으로 가지고 있는 인터페이스가 없다면 타입은 서로 호환되지 않는다.

반면, 타입스크립트는 이름이 아닌 구조로 타입을 구분하는데 이것을 구조적 타이핑이라고 한다.

예제 2.

interface Developer {
  faceValue: number;
}

interface BankNote {
  faceValue: number;
}

let developer: Developer = { faceValue: 52 };
let bankNote: BankNote = { faceValue: 10000 };

developer = bankNote; // OK
bankNote = developer; // OK

예를 들어, DeveloperBankNote 인터페이스는 모두 faceValue라는 같은 속성을 가지고 있다.

이 두 타입은 서로 다른 이름을 가지고 있지만, 구조적으로 동일하기 때문에 타입스크립트 같은 구조적 타입 시스템을 사용하는 언어에서는 서로 호환될 수 있다.

구조적 서브타이핑

구조적 서브타이핑은 타입스크립트에서 타입을 구분하는 중요한 개념으로, 객체가 가진 속성을 바탕으로 타입을 구분한다.

이를 통해 타입 간의 호환성을 구조적으로 판단하며, 타입 계층 구조에 구애 받지 않는 유연한 타입 시스템을 구현할 수 있다.

예제 1.

interface Pet {
  name: string
}

interface Cat {
  name: string
  age: number
}

let pet: Pet;
let cat: Cat = { name: "Zag", age: 2 };

// ✅ OK
pet = cat;

CatPet과 다른 타입이지만, Pet이 갖고 있는 name이라는 속성을 가지고 있기 때문에 Cat 타입의 변수를 Pet 타입의 변수에 할당할 수 있다.

예제 2.

구조적 서브 타이핑은 함수의 매개변수에도 적용된다.

interface Pet {
  name: string
}

let cat = { name: "Zag", age: 2 };

function greet(pet: Pet) {
  console.log(`Hello, ${pet.name}`);
}

greet(cat); // ✅ OK

greet() 함수의 매개변수에 들어갈 수 있는 값은 Pet 타입으로 제한되어 있어도, Cat타입의 객체를 인자로 전달할 수 있다.

cat 객체는 Pet 인터페이스가 가지고 있는 name 속성을 가지고 있어 pet.name의 방식으로 name 속성에 접근할 수 있기 때문이다.

예제 3.

구조적 서브 타이핑은 타입의 상속에도 적용된다.

class Person {
  name: string;

  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

class Developer {
  name: string;

  age: number;

  sleepTime: number;

  constructor(name: string, age: number, sleepTime: number) {
    this.name = name;
    this.age = age;
    this.sleepTime = sleepTime;
  }
}

function greet(p: Person) {
  console.log(`Hello, I'm ${p.name}`);
}

const developer = new Developer("zig", 20, 7);

greet(developer); // Hello, I'm zig

Developer 클래스가 Person 클래스를 상속받지 않아도 Person이 갖고 있는 속성을 가지고 있기 때문에 greet() 함수에 Developer 객체를 인자로 전달할 수 있다.

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

명목적 타이핑은 객체의 속성을 다른 객체의 속성과 호환되지 않도록 하여 안전성을 추구한다.

그러나 타입스크립트가 구조적 타이핑을 채택한 이유는 덕 타이핑을 기반으로하는 자바스크립트를 모델링한 언어이기 때문이다.

덕 타이핑
어떤 타입에 부합하는 변수와 메서드를 가질 경우 해당 타입에 속하는 것으로 간주하는 방식이다.
"만약 어떤 새가 오리처럼 걷고, 헤엄치며 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다"

타입스크립트는 이 특징을 받아들여 더욱 유연한 타이핑을 제공하며 쉬운 사용성과 안정성을 동시에 추구한다.

두 가지 타이핑 방식은 모두 객체의 변수, 메서드 같은 필드를 기반으로 타입을 검사하지만 다음과 같은 차이점을 가진다.

덕 타이핑

  • 런타임에 타입을 검사한다.
  • 주로 동적 타이핑에서 사용된다.

구조적 타이핑

  • 컴파일 타임에 타입체커가 타입을 검사한다.
  • 정적 타이핑에서 사용된다.

구조적 타이핑의 결과

타입스크립트의 구조적 타이핑의 특징때문에 예기치 못한 결과가 나올 때도 있다.

예제 1.

interface Cube {
  width: number
  height: number
  depth: number
}

function addLines(c: Cube) {
  let total = 0;

  for (const axis of Object.keys(c)) {
    // 🚨 Element implicitly has an 'any' type
    // because expression of type 'string' can't be used to index type 'Cube'.
    // 🚨 No index signature with a parameter of type 'string'
    // was found on type 'Cube'
    const length = c[axis];

    total += length;
  }
}

Cube 인터페이스의 모든 필드는 number 타입을 가지지만, c에 들어올 객체는 Cubewidth, height, depth 외에도 어떤 속성이든 가질 수 있기 때문에 c[axis]의 타입이 string일 수도 있어 에러가 발생한다.

즉, 아래와 같은 상황이다.

const namedCube = {
  width: 6,
  height: 5,
  depth: 4,
  name: "SweetCube", // string 타입의 추가 속성이 정의되었다
};

addLines(namedCube); // ✅ OK

이처럼 타입스크립트는 c[axis]가 어떤 속성을 지닐지 알 수 없으며 c[axis]의 타입을 number라고 확정할수 없어서 에러를 발생시킨다. 구조적 타이핑의 특징으로 Cube타입 값이 들어갈 곳에 name같은 추가 속성을 가진 객체도 할당할 수 있기 때문에 발생하는 문제이다.

이러한 한계를 극복하고자 타입스크립트는 명목적 타이핑 언어의 특징을 결합한 식별 가능한 유니온같은 방법을 도입했다.

타입스크립트의 점진적 타입 확인

점진적 타입 검사란 컴파일 타임에 타입을 검사하면서 필요에 따라 타입 선언 생략을 허용하는 방식이다. 타입을 지정한 변수와 표현식은 정적으로 타입을 검사하지만 타입 선언이 생략되면 동적으로 검사를 수행한다.

타입 선언을 생략하면 암시적 타입 변환이 일어난다.

function add(x, y) {
  return x + y;
}

// 위 코드는 아래와 같이 암시적 타입 변환이 일어난다.
function add(x: any, y: any): any

모든 변수와 표현식의 타입을 컴파일타임에 검사하지 않아도 되기 때문에 타입이 올바르게 정해지지 않으면 런타임에서 에러가 발생하기도 한다.

const names = ["zig", "colin"];
console.log(names[2].toUpperCase());
// 🚨 TypeError: Cannot read property 'toUpperCase' of undefined

값 VS 타입

const 변수: 타입 = 값;

타입스크립트 문법인 type으로 선언한 내용은 자바스크립트 런타임에서 제거되어 값 공간과 타입 공간은 서로 충돌하지 않는다. 따라서 타입과 변수를 같은 이름으로 정의할 수 있다.

예제 1.

type Developer = { isWorking: true };
const Developer = { isTyping: true }; // OK

type Cat = { name: string; age: number };
const Cat = { slideStuffOffTheTable: true }; // OK

타입스크립트는 개발자가 작성한 코드의 문맥을 파악해서 스스로 값 또는 타입으로 해석하는데, 둘의 구분은 맥락에 따라 달라지기 때문에 값 공간과 타입 공간을 혼동할 때도 있다.

예제 2.

function email(options: { person: Person; subject: string; body: string }) {
  // ...
}

email 함수는 options라는 하나의 매개변수를 받는데, options{ person: Person; subject: string; body: string } 형태의 객체이다.

function email({ person, subject, body }) {
  // ...
}

이 코드는 객체 구조 분해 할당을 사용하여 매개변수를 받는다. email 함수는 여전히 객체를 매개변수로 받지만, 이 객체의 각 속성(person, subject, body)은 함수 내에서 직접 사용될 수 있다.

그러나 같은 코드를 타입스크립트에서 구조 분해 할당하면 오류가 발생한다.

function email({
  person: Person, // 🚨
  subject: string, // 🚨
  body: string, // 🚨
}) {
  // ...
}

Personstring이 타입이 아닌 값으로 해석되기 때문이다.

올바른 작성법은 다음과 같다.

function email({
  person,
  subject,
  body,
}: {
  person: Person;
  subject: string;
  body: string;
  }) {
  // ...
}

타입스크립트에서는 값과 타입 공간에 동시에 존재하는 심볼도 있다.
대표적인 것이 클래스와 enum이다.

클래스

클래스는 객체 인스턴스를 더욱 쉽게 생성하기 위한 문법 기능으로 실제 동작은 함수와 같은데, 동시에 타입으로도 사용된다.

예제 1.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

const rect1 = new Rectangle(5, 4);
class Developer {
  name: string;

  domain: string;

  constructor(name: string, domain: string) {
    this.name = name;
    this.domain = domain;
  }
}

const me: Developer = new Developer("zig", "frontend");

변수명 me 뒤에 등장하는 : Developer에서 Developer는 타입에 해당하지만, new 키워드 뒤의 Developer는 클래스의 생성자 함수인 값으로 동작한다.

타입스크립트에서 클래스는 타입 애너테이션으로 사용할 수 있지만 런타임에서 객체로 변환되어 자바스크립트의 값으로 사용되는 특징을 가지고 있다.

enum

마찬가지로 enum 역시 런타임에 객체로 변환되는 값이다.
enum은 런타임에 실제 객체로 존재하며, 함수로 표현할 수도 있다.

예제 1.

enum Direction {
  Up, // 0
  Down, // 1
  Left, // 2
  Right, // 3
  }
// 순수 자바스크립트 코드로 컴파일한 결과
let Direction;
(function (Direction) {
  Direction[(Direction.Up = 0)] = "Up";
  Direction[(Direction.Down = 1)] = "Down";
  Direction[(Direction.Left = 2)] = "Left";
  Direction[(Direction.Right = 3)] = "Right";
}(Direction || (Direction = {})));

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

예제 2.

// enum이 타입으로 사용된 경우
enum WeekDays {
  MON = "Mon",
  TUES = "Tues",
  WEDNES = "Wednes",
  <THURS = "Thurs",
  FRI = "Fri",
}
// ‘MON’ | ‘TUES’ | ‘WEDNES’ | ‘THURS’ | ‘FRI’
type WeekDaysKey = keyof typeof WeekDays;

function printDay(key: WeekDaysKey, message: string) {
  const day = WeekDays[key];
  if (day <= WeekDays.WEDNES) {
    console.log(`It’s still ${day}day, ${message}`);
  }
}

printDay("TUES", "wanna go home");
// enum이 값 공간에서 사용된 경우
enum MyColors {
  BLUE = "#0000FF",
  YELLOW = "#FFFF00",
  MINT = "#2AC1BC",
}

function whatMintColor(palette: { MINT: string }) {
  return palette.MINT;
}

whatMintColor(MyColors); // ✅

타입스크립트에서 자바스크립트의 키워드가 해석되는 방식

타입스크립트에서 어떠한 심볼이 값으로 사용된다는 것은 컴파일러를 사용해서 타입스크립트 파일을 자바스크립트 파일로 변환해도 여전히 자바스크립트 파일에 해당 정보가 남아있음을 의미한다.

반면 타입으로만 사용되는 요소는 컴파일 이후에 자바스크립트 파일에서 해당 정보가 사라진다.

키워드타입
classYY
const, let, varYN
enumYY
functionYN
interfaceNY
typeNY
namespaceYN

타입을 확인하는 방법

typeof

typeof는 연산하기 전에 피연산자의 데이터 타입을 나타내는 문자열을 반환한다.

예제 1.

typeof 2022; // "number"
typeof "woowahan"; // "string"
typeof true; // "boolean"
typeof {}; // "object"

또한, typeof 연산자도 값에서 쓰일 때와 타입에서 쓰일 때의 역할이 다르다.

  • 값에서 사용될 때 : 자바스크립트 런타임의 typeof 연산자가 된다.
  • 타입에서 사용될 때 : 값을 읽고 타입스크립트 타입을 반환한다.

예제 2.

interface Person {
  first: string;
  last: string;
}

const person: Person = { first: "zig", last: "song" };

function email(options: { person: Person; subject: string; body: string }) {}
// 값에서 사용될 때
const v1 = typeof person; // 값은 ‘object’ 
const v2 = typeof email; // 값은 ‘function’
// 타입에서 사용될 때
type T1 = typeof person; // 타입은 Person
type T2 = typeof email; // 타입은 (options: { person: Person; subject: string; body:string; }) = > void

자바스크립트의 클래스는 typeof 연산자를 쓸 때 주의해야 한다.

예제 3.

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

타입 공간에서 typeof Developer의 반환값은 조금 특이한데 type T에 할당된 Developer가 인스턴스의 타입이 아니라 new 키워드를 사용할 때 볼 수 있는 생성자 함수이기 때문이다.

const zig: Developer = new Developer("zig", 7);
type ZigType = typeof zig; // 타입이 Developer

Developer 클래스로 생성한 zig 인스턴스는 Developer가 인스턴스 타입으로 생성되었기 때문에 타입 공간에서의 typeof zig 즉, type ZigTypeDeveloper를 반환한다.

그러나 DevloperDeveloper 타입의 인스턴스를 만드는 생성자 함수이다. 따라서 typeof Developer 타입도 그 자체인 typeof Developer가 된다. typeof Developer를 풀어서 설명하면 다음과 같다.

new (name: string, sleepingTime: number): Developer

zig는 Developer 클래스의 인스턴스이므로, typeof zig는 Developer 타입을 반환한다.

instanceof

instanceof 연산자는 객체가 특정 클래스나 생성자 함수의 인스턴스인지 여부를 확인하는 데 사용된다.

typeof 연산자처럼 instanceof 연산자의 필터링으로 타입이 보장된 상태에서 안전하게 값의 타입을 정제하여 사용할 수 있다.

예제

let error: unknown;

if (error instanceof Error) {
  // 이 블록 내에서 error는 Error 타입으로 정제되어 사용된다.
  showAlertModal(error.message); // // 안전하게 Error 클래스의 메소드를 사용할 수 있음
} else {
  // error가 Error 타입이 아닌 경우의 처리
  throw Error(error);
}

타입 단언

as 키워드를 사용해 타입을 강제할 수 있는데, 이는 개발자가 해당 값의 타입을 더 잘 파악할 수 있을 때 사용되며 강제 형 변환과 유사한 기능을 제공한다.

타입 시스템과 문법은 컴파일 단계에서 제거되기 때문에 타입 단언이 형 변환을 강제할 수 있지만 런타임에서는 효력을 발휘하지 못한다.

예제

const loaded_text: unknown; // 어딘가에서 unknown 타입 값을 전달받았다고 가정

const validateInputText = (text: string) => {
  if (text.length < 10) return "최소 10글자 이상 입력해야 합니다.";
  return "정상 입력된 값입니다.";
};

validateInputText(loaded_text as string); // as 키워드를 사용해서 string으로 강제하지 않으면 타입스크립트 컴파일러 단계에서 에러 발생

타입 가드

특정 조건을 검사해서 타입을 정제하고 타입 안정성을 높이는 패턴이다.

2.3 원시 타입

원시 값과 원시 래퍼 객체
자바스크립트에서는 원시 값에 대응하는 원시 래퍼 객체가 있지만, 타입스크립트에서는 원시 값과 원시 래퍼 객체를 구분하여 사용한다.
타입스크립트에서는 원시 값에 대응하는 타입을 소문자로 표기하며, 파스칼 표기법을 사용하면 해당 원시 값을 래핑하는 객체 타입을 의미한다.
따라서, 타입스크립트에서는 원시 값과 원시 래퍼 객체를 구분하여 사용해야 한다.

boolean

오직 trueflase 값만 할당할 수 있는 boolean 타입이다.

예제

const isEmpty: boolean = true;
const isLoading: boolean = false;

// errorAction.type과 ERROR_TEXT가 같은지 비교한 결괏값을 boolean 타입으로 반환하는 함수
function isTextError(errorCode: ErrorCodeType): boolean {
  const errorAction = getErrorAction(errorCode);
  if (errorAction) {
    return errorAction.type === ERROR_TEXT;
  }
  return false;
}

자바스크립트에는 boolean 원시 값은 아니지만 형 변환을 통해 true / false로 취급되는 Tryuthy / Falsy같이 존재하는데, 이 값은 boolean 원시 값이 아니므로 타입스크립트에서도 boolean 타입에 해당하지 않는다.

undefined

오직 undefined 값만 할당할 수 있으며, 초기화되어 있지 않거나 존재하지 않음을 나타낸다.

예제

let value: string;
console.log(value); // undefined (값이 아직 할당되지 않음)

type Person = {
  name: string;
  job?: string;
};

위 코드에서 Person타입의 job 속성은 옵셔널로 지정되어 있는데 이런 경우에도 undefined를 할당할 수 있다.

null

오직 null만 할당할 수 있다.

예제

let value: null | undefined;
console.log(value); // undefined (값이 아직 할당되지 않음)

value = null;
console.log(value); // null
type Person1 = {
  name: string;
  job?: string; // job이라는 속성이 있을 수도 또는 없을 수도 있음
};

type Person2 = {
  name: string;
  job: string | null; // 속성을 가지고 있지만 값이 비어있을 수 있음 (무직인 상태)
};

number

자바스크립트의 숫자에 해당하는 모든 원시 값을 할당할 수 있다.

예제

const maxLength: number = 10;
const maxWidth: number = 120.3;
const maximum: number = +Infinity;
const notANumber: number = NaN;

bigint

ES2020에서 새롭게 도입된 데이터 타입으로 타입스크립트 3.2 버전부터 사용할 수 있다.

number 타입과 bigint 타입은 엄연히 서로 다른 타입이기 때문에 상호작용을 불가능하다.

예제

const bigNumber1: bigint = BigInt(999999999999);
const bigNumber2: bigint = 999999999999n;

string

문자열을 할당할 수 있는 타입으로, 공백도 해당된다.

예제

const receiverName: string =KG;
const receiverPhoneNumber: string =010-0000-0000;
const letterContent: string = `안녕, 내 이름은 ${senderName}이야.`;

symbol

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

타입스크립트에서는 symbol 타입과 const 선언에서만 사용할 수 있는 unique symbol 타입이라는 symbol의 하위 타입도 있다.

예제

const MOVIE_TITLE = Symbol("title");
const MUSIC_TITLE = Symbol("title");
console.log(MOVIE_TITLE === MUSIC_TITLE); // false

let SYMBOL: unique symbol = Symbol(); // A variable whose type is a 'unique symbol'
// type must be 'const'

# 2.4 객체 타입 원시 타입에 속하지 않는 값은 모두 객체 타입으로 분류할 수 있다. ## object **object** 타입은 `any` 타입과 유사하게 객체에 대항하는 모든 타입 값을 유동적으로 할당할 수 있어 정적 타이핑의 의미가 크게 퇴색되기 때문에 가급적 사용하지 말도록 권장되는 타입이다.

다만 any와는 다르게 원시 타입에 해당하는 값은 object 타입에 속하지 않는다.

예시

function isObject(value: object) {
  return (
    Object.prototype.toString.call(value).replace(/\[|\]|\s|object/g, "") === "Object"
  );
}
// 객체, 배열, 정규 표현식, 함수, 클래스 등 모두 object 타입과 호환된다
isObject({});
isObject({ name: "KG" });
isObject([0, 1, 2]);
isObject(new RegExp("object"));
isObject(() => {
  console.log("hello wolrd");
});
isObject(class Class {});
// 그러나 원시 타입은 호환되지 않는다
isObject(20); // false
isObject("KG"); // false

{}

타입스크립트에서는 객체의 각 속성에 대한 타입을 중괄호 {} 안에 지정할 수 있다.

예제 1.

// 정상
const noticePopup: { title: string; description: string } = {
  title: "IE 지원 종료 안내",
  description: "2022.07.15일부로 배민상회 IE 브라우저 지원을 종료합니다.",
};

// SyntaxError
const noticePopup: { title: string; description: string } = {
  title: "IE 지원 종료 안내",
  description: "2022.07.15일부로 배민상회 IE 브라우저 지원을 종료합니다.",
  startAt: "2022.07.15 10:00:00", // startAt은 지정한 타입에 존재하지 않으므로 오류
};

빈 객체를 생성할 때도 {}를 사용할 수 있지만, {} 타입으로 지정된 객체에는 어떤 값도 속성으로 할당할 수 없다.

예제 2.

let noticePopup: {} = {};

noticePopup.title = "IE 지원 종료 안내"; // (X) title 속성을 지정할 수 없음

{} 타입으로 지정된 객체는 완전히 비어있는 순수한 객체를 의미하는 것이 아니다.

그러나 자바스크립트의 프로토타입 체이닝 때문에 아래와 같이 기본 Object 객체의 메서드는 사용할 수 있다.

console.log(noticePopup.toString()); // [object Object]

이와 같은 이유로 타입스크립트에서 객체 래퍼를 타입으로 지정할 수 있는데도 소문자로 된 타입스크립트 타입 체계를 사용하는 게 일반적이다.

array

타입스크립트에서는 배열을 array라는 별도 타입으로 다루며, 하나의 타입 값만 가질 수 있다는 점에서 자바스크립트 배열보다 조금 더 엄격하다. 하지만 원소 개수는 타입에 영향을 주지 않는다.

타입스크립트에서 배열 타입을 선언하는 방식은 Array 키워드로 선언하거나 []를 사용해서 선언하는 방법이 있다.

예제 1.

const getCartList = async (cartId: number[]) => {
  const res = await CartApi.GET_CART_LIST(cartId);
  return res.getData();
};

getCartList([]); // (O) 빈 배열도 가능하다
getCartList([1001]); // (O)
getCartList([1001, 1002, 1003]); // (O) number 타입 원소 몇 개가 들어와도 상관없다
getCartList([1001, "1002"]); // (X) ‘1002’는 string 타입이므로 불가하다

주의해야 할 점은 튜플 타입도 대괄호로 선언한다는 것이다.

타입스크립트 튜플 타입은 배열과 유사하지만 튜플의 대괄호 내부에는 선언 시점에 지정해준 타입 값만 할당할 수 있으며 원소 개수도 타입 선언 시점에 미리 정해진다.

이것은 객체 리터럴에서 선언하지 않은 속성을 할당하거나, 선언한 속성을 할당하지 않았을 때 에러가 발생한다는 점과 비슷하다.

예제 2.

const targetCodes: ["CATEGORY", "EXHIBITION"] = ["CATEGORY", "EXHIBITION"]; // (O)
const targetCodes: ["CATEGORY", "EXHIBITION"] = [
  "CATEGORY",
  "EXHIBITION",
  "SALE",
]; // (X) SALE은 지정할 수 없음

type과 interface 키워드

흔히 객체를 타이핑하기 위해 자주 사용하는 키워드로 typeinterface가 있다.

type NoticePopupType = {
  title: string;
  description: string;
};

interface INoticePopup {
  title: string;
  description: string;
}
const noticePopup1: NoticePopupType = { /* ... */ };
const noticePopup2: INoticePopup = { /* ... */ };

function

자바스크립트에서는 함수도 일종의 객체로 간주하지만 typeof 연산자로 함수 타입을 출력하면 function이라는 별도의 타입으로 분류한다는 것을 알 수 있다.

예제 1.

function add(a, b) {
  return a + b;
}

console.log(typeof add); // ‘function’

마찬가지로 타입스크립트에서도 함수를 별도의 함수 타입으로 지정할 수 있다.

함수 타입 지정시 주의할 점

  • 자바스크리븥에서 typeof 연산자로 확인한 function 이라는 키워드 자체를 타입으로 사용하지 않는다.
  • 함수는 매개변수 목록을 받을 수 있는데 타입스크립트에서는 매개변수도 별도의 타입으로 지정해야 한다.

예제 2.

function add(a: number, b: number): number {
  return a + b;
}

그런데, 함수 자체의 타입은 어떻게 지정할 수 있을까? 호출 시그니처를 정의하는 방식을 사용하면 된다.

호출 시그니처(Call Signature)
타입스크립트에서 함수 타입을 정의할 때 사용하는 문법이다.
함수 타입은 해당 함수가 받는 매개변수와 반환하는 값의 타입으로 결정된다.
호출 시그니처는 이러한 함수의 매개변수와 반환 값의 타입을 명시하는 역할을 한다.

예제 3.

type add = (a: number, b: number) => number;

타입스크립트에서 함수의 타입을 명시할 때는 화살표 함수 방식으로 호출 시그니처를 정의한다.

이 방식은 자바스크립트의 화살표 함수와 유사하며 함수의 입력 파라미터와 반환 값의 타입을 명시할 수 있다.

profile
해내는 사람

0개의 댓글