TypeScript - 배열과 튜플, 객체 타입

Minkyu Shin·2023년 6월 13일
0

TypeScript

목록 보기
4/7
post-thumbnail

TypeScript

본 내용은 한 입 크기로 잘라먹는 타입스크립트 강의를 바탕으로 작성되었습니다. 사진 자료의 출처 또한 위 강의입니다.

배열과 튜플

배열은 JS의 배열과 크게 다르지 않고 튜플은 타입스크립트에서만 제공되는 타입이다.

배열 (Array)

타입스크립트에서 배열 타입을 정의하는 방법은 다음과 같다.

한가지 타입 요소를 갖는 배열 타입 정의

let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];

변수의 이름 뒤에 콜론을 작성한 후 배열요소의타입[] 형식 또는 Array<배열 요소의 타입> 으로 배열 타입을 정의하면 된다.
(이 때 꺽쇠 (<>)와 함께 타입을 작성하는 문법을 제네릭이라고 한다.)
따라서 불리언 값이 담긴 배열을 정의한다면 다음과 같이 작성하면 된다.

let boolArr1: boolean[] = [true, false, true];
let boolArr2: Array<boolean> = [true, false, true];

다양한 타입 요소를 갖는 배열 타입 정의

문자열, 숫자, 불리언 등 다양한 타입을 가진 요소가 들어있는 배열의 타입은 어떻게 정의해야 할까?

let multArr: (number | string | boolean)[] = [1, 'hello', true];

이렇게 소괄호와 바 (|) 를 사용하여 포함된 배열 요소의 모든 타입들을 작성해주면 된다. 위와 같은 코드에서는 배열 요소의 타입이 number이거나 string이거나 boolean 타입임을 말한다.
바를 이용하여 여러 타입 중 하나를 만족하는 타입을 정의하는 문법을 유니온 타입이라고 한다.

다차원 배열 타입 정의하기

대괄호 ([])를 연달아 작성하면 다차원 배열 타입을 정의할 수 있다.

let dupArr: number[][] = [
  [1, 2, 3],
  [4, 5],
];

튜플 (Tuple)

튜플 타입은 JS에는 없고 타입스크립트에만 있는 특수한 타입이다. 배열 중에 길이와 타입이 고정된 배열을 튜플이라고 부른다. 그렇다면 길이를 고정시킬 수 있는 방법은 무엇일까? 다음 예시 코드를 보자.

let tup: [string, number] = ['hello', 10];

배열의 타입 정의와는 조금 다르게 배열 안에 직접 요소의 타입들을 나열해 두면 된다. 배열 안에 정의된 타입의 숫자만큼 길이가 고정되고 순서 또한 정의된 순서를 따라야 한다. 예를 들어 [string, number] 로 정의된 튜플에 [1, 'hello'] 와 같은 값을 할당할 수는 없다.

튜플은 결국 배열이다

튜플 타입이 정의된 타입스크립트 파일을 컴파일 해보면 JS 배열로 변환되는 것을 확인할 수 있다. 따라서, 튜플에는 배열의 메소드를 모두 사용할 수 있다.

let tup: [number, number] = [1, 2];

tup.push(3); // No error
tup.push(4); // No error
tup.push(5); // No error

하지만 길이를 보장하는 특징을 가지는 튜플을 제대로 활용하기 위해서는 pop() , push() 등의 길이를 무시하고 요소를 추가하거나 삭제하는 메소드를 조심스럽게 사용해야 할 필요가 있다.

튜플의 사용 이유

예시를 통해 왜 튜플을 사용하는지에 대해 알아보자. 유저 정보를 저장하는 2차원 배열이 있다. 각 배열의 첫 요소로는 유저의 이름, 두번째 요소로는 회원번호가 저장되어 있다.

let users = [
  ['user1', 1],
  ['user2', 2],
  ['user3', 3],
  ['user4', 4],
]

이 값을 활용하여 다른 작업을 처리하고 있었는데, 다른 개발자가 갑자기 다음과 같은 값을 배열에 추가하였다.

users.push([5, 'user5']);

배열 요소의 순서가 뒤바뀐 채 추가되면 프로그램에 문제가 생길 수 있다. 이 때 튜플이 큰 역할을 하게 되는데 다음과 같이 튜플로 정의하게 되면,

let users: [string, number][] = [
  ['user1', 1],
  ['user2', 2],
  ['user3', 3],
  ['user4', 4],
  [5, 'user5'], // Error
]

타입스크립트에서 오류를 발생시키므로 발생할 수 있는 문제를 조기에 해결할 수 있게 된다.

객체

객체 타입의 정의

object로 정의하기

가장 기본적으로 객체 타입을 정의하는 방식은 object 로 타입을 정의하는 것이다. 다음 코드가 object 타입으로 객체를 정의한 예시이다.

let example: object = {
  prop1: 1,
  prop2: 'hello',
};

하지만, 이렇게 객체를 정의하고 example.prop1 으로 객체의 속성에 접근하려고 하면 오류가 발생한다.

example이라는 객체에 prop1 이라는 이름을 가진 프로퍼티가 존재하는데 왜 이런 오류가 발생할까? 그건 바로 타입스크립트의 object 타입은 값이 객체임을 표현하는 것 외에는 아무 정보도 제공하지 않기 때문이다. 결국 우리가 example 객체에 정의해 준 타입은 프로퍼티에 대한 정보를 전혀 가지고 있지 않아 접근하려 하면 오류가 발생하는 것이다.

우리가 원하는 대로 변수에 저장된 객체 구조를 그대로 타입으로 만들고 싶을 때 사용하는 것이 바로 객체 리터럴 타입이다.

객체 리터럴로 정의하기

객체 리터럴은 중괄호를 열고 객체가 갖는 프로퍼티를 직접 나열해 만드는 타입이다.

let example : {
  prop1: number;
  prop2: string;
} = {
  prop1: 1,
  prop2: 'hello',
};

example.prop1 // No error

변수의 타입을 객체 리터럴로 정의하여 객체 내의 프로퍼티들의 타입을 각각 나열해 주면 이제 문제없이 프로퍼티에 접근할 수 있게 된다. 점 표기법 뿐만 아니라 대괄호 표기법을 통해서도 프로퍼티에 잘 접근된다.

또한 각 프로퍼티 별 타입을 정의해 주었으므로 프로퍼티 값의 타입도 잘 나타나게 된다.

타입스크립트는 '구조적 타입 시스템'을 사용한다. 이에 따라, 한 객체에 어떤 프로퍼티들이 있어야 하는지를 정의하는 방식으로 객체의 타입을 정의하게 된다. 위 예시에서 prop1prop2 가 존재하는 객체로 정의한 것과 같이 말이다.

특수한 프로퍼티 정의하기

타입스크립트에서 프로퍼티를 정의할 때 특정 프로퍼티를 선택적이거나 읽기 전용으로 만들어 줄 수 있다.

선택적 프로퍼티 (Optional Property)

객체를 사용할 때 특정 프로퍼티는 있어도 없어도 되는 상황이 발생할 수 있다. 만약 다음과 같이 객체 타입을 정의하고 프로퍼티 중 한가지가 없는 객체를 할당하게 되면 오류가 발생한다.

let example : {
  prop1: number;
  prop2: string;
} = {
  prop1: 1,
  prop2: 'hello',
};

example = {
  prop1: 10,
}; // Error

객체 example의 타입은 prop1prop2 가 존재하는 객체인데, prop2 가 존재하지 않는 객체를 할당해 주었으니 당연히 오류가 발생하는 것이다.

만약 상황에 따라 특정 프로퍼티를 생략할 수 있도록 객체 타입을 정의해 주고 싶다면 선택적 프로퍼티를 사용하면 된다. 선택적 프로퍼티는 프로퍼티 이름 뒤에 물음표(?)를 붙여주는 문법을 통해 사용할 수 있다.

let example : {
  prop1: number;
  prop2?: string; // prop2는 이제 선택적 프로퍼티
} = {
  prop1: 1,
  prop2: 'hello',
};

example = {
  prop1: 10,
}; // Ok

이제 prop2 는 반드시 존재할 필요는 없는 프로퍼티가 되었다. 단, 만약 prop2 가 존재한다면 타입 정의 시 선택한 타입의 값을 가져야 한다. 가령 prop2 의 값으로 number 타입의 값을 넣어주면 오류가 발생한다.

읽기 전용 프로퍼티 (Readonly Property)

객체의 특정 프로퍼티를 읽기 전용으로 만들어 변경할 수 없게 하고 싶다면 프로퍼티 이름 앞에 readonly 키워드를 붙여주면 된다.

let example : {
  readonly prop1: number;
  prop2: string;
} = {
  prop1: 1,
  prop2: 'hello',
};

example.prop1 = 10; // Error

prop1 은 읽기 전용 프로퍼티로 정의 되었기 때문에 더이상 값을 수정할 수 없다. 읽기 전용 프로퍼티를 사용하여 의도치 않은 프로퍼티 수정을 막을 수 있다.

profile
개발자를 지망하는 경영학도

0개의 댓글