Post

TypeScript 를 통해 객체와 배열의 타입을 지정하는 방법

이전에 타입스크립트를 통해 타입을 지정하고, 보다 편리하게 추론하는 것까지 알아보았다. 이번엔 객체와 배열의 타입을 지정할 때 알면 좋은 것을 간단히 짚고 넘어가보도록 하자.

객체의 타입을 지정할 때

1
2
const obj = { name: 'zero' }; // const obj: { name: string }
const arr = [1, 3, 'five']; // const arr: (string | number)[]

obj 를 먼저 살펴보면 const 로 선언하더라도, obj 의 속성인 name 의 타입은 string 으로 추론한다. 사실, const 라고 하더라도 속성에 할당한 값은 변경이 가능하기 때문에 그렇다.

arr 의 경우, (string | number)[] 라는 타입이 있다. 이는 나중에 다루도록 해보자.

객체에 할당한 속성의 타입을 고정하는 법

만일, obj 에 할당한 것들의 내용을 변경하지 않는다는 것이 확실하다면 as const 접미사를 사용하면 된다.

1
2
const obj = { name: 'zero' } as const; // const obj: { readonly name: 'zero' }
const arr = [1, 3, 'five'] as const ; // const arr: readonly [1, 3, 'five']

이렇게 하고 나면, readonly 라는 수식어가 나타나고 리터럴 타입으로 변환된다. readonly 수식어가 있으면, 타입스크립트에서 객체의 속성 변경 또한 통제가 가능하니 참고하자.

배열의 타입 지정

직전에 배열도 잠깐 다루긴 했지만, 조금 더 살펴볼 내용이 있다.

배열의 타입을 지정할 때는, 아래와 같이 표기하면 된다.

1
2
3
4
const arr1: string[] = ['str1', 'str2', 'str3'];
const arr2: Array<number> = [1, 2, 3];

arr1.push(1) // Argument of type 'number' is not assignable to parameter of type 'string'

한 가지 자료형만 있는 배열의 경우, arr1arr2 처럼 두 방법 모두 사용이 가능하다. arr2 의 타입을 지정할 때 사용한 <> 는 제네릭이라고 부르는데, 추후에 다뤄보자.

배열의 경우도 당연히 타입 추론이 가능하고, 원소들의 타입을 보고 추론을 하게 된다.

튜플(tuple)

타입스크립트를 통해, 배열의 각 인덱스 마다 타입을 지정할 수 있다. 이렇게 각 원소의 자리에 타입이 고정된 배열을 튜플(tuple) 이라고 한다.

코드로 살펴보자.

1
2
3
4
5
6
const tuple: [number, boolean, string] = [1, false, 'hi'];
tuple[0] = 3;
tuple[2] = 5; // Type 'number' is not assignable to type 'string'

tuple[3] = 'no'; // Type '"no"' is not assgnable to type 'undefined'
tuple.push('yes');

위 튜플은 이렇게 동작한다.

2번째 인덱스는 string 으로 지정했기에, 할당이 불가능하다. 그렇다면, 3번째 인덱스에는 할당이 안되는 이유는 무엇일까?

튜플은 각 자리에 타입이 고정되어있다고 했다. 지금은 0 - 2 번째 까지만 타입이 지정되어 있고, 그 이후부터는 타입을 명시하지 않았기에 undefined 로 추론을 하고 인덱스 기반 접근을 막는 것이다.

다만, 아이러니하게도 push() 메서드를 막지는 못한다. 만약 이것도 막고 싶다면, readonly 를 직접 사용하면 된다!

1
2
3
const tuple: readonly [number, boolean, string] = [1, false, 'hi'];

tuple.push('no'); // Property 'push' does not exst on type 'readonly [number, boolean, string]'.

이렇게 하면, 마치 배열의 길이를 고정한 것과 같은 효과를 보이는데 사실 고정된 것은 아니다. 특정한 위치의 값들만 고정하고, 이후 같은 값들이 나온다고 명시하고 싶다면 이렇게 하면 된다.

1
2
3
const strNumBools: [string, number, ...boolean[]] = ['hi', 123, false, true, false];
const strNumsBool: [string, ...number[], boolean] = ['hi', 123, 456, 789, false];
const strsNumBool: [...string[], number, boolean] = ['str', 'str', 'str', 123, true];

바로 스프레드 연산자를 활용하는 것인데, 몇 가지 예시를 더 보자.

1
2
3
4
5
6
const arr1 = ['hi', true];
const arr2 = [45, ...arr1] // const arr2: (string | number | boolean)[]

// 구조분해할당
const [a, ...rest1] = ['hi', 1, 23, 456];
const [b, ...rest2]: [string, ...number[]] = ['hi', 1, 23, 456]; // 타입 명시

이런 방식으로도 타입 추론이 동작한다.

?(옵셔널 수식어)

말 그대로, 타입에 ? 를 붙일 수 있다. 해당 값이 있어도 되고, 없다고 해서 오류를 만들지 않는다.

1
2
3
4
5
let tuple: [number, boolean?, string?] = [1, false, 'hi'];

tuple = [3, true];
tuple = [6];
tuple = [7, 'no']; // Type 'string' is not assignable to type 'boolean | undefined'.

해당 위치에 다른 타입의 값이 들어오는게 아니라면, 보다 유연하게 활용이 가능하니 참고하자.

This post is licensed under CC BY 4.0 by the author.