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'
한 가지 자료형만 있는 배열의 경우, arr1
과 arr2
처럼 두 방법 모두 사용이 가능하다. 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'.
해당 위치에 다른 타입의 값이 들어오는게 아니라면, 보다 유연하게 활용이 가능하니 참고하자.