
자바스크립트에서 배열은 객체에 속하는 타입으로 분류하며 단독으로 배열이라는 자료형에 국한하지 않는다. 타입스크립트에서 Array라는 타입을 사용하기 위해서는 타입스크립트의 특수한 문법을 사용해야한다.
자바스크립트의 배열은 동적 언어의 특징에 따라 어떤 값이든 배열의 원소로 허용한다. 하지만 이러한 개념은 타입스크립트의 정적 타이핑과 부합하지 않는다. 대개 정적타입의 언어에서는 배열을 선언할 때 크기까지 동시에 제한하기도 한다. 자바, C++ 같은 다른 정적 언어에서도 배열의 원소로 하나의 타입만 사용하도록 명시한다. 타입스크립트에서는 배열의 크기까지 제한하지는 않지만 정적 타입의 특성을 살려 명시적인 타입을 선언함으로서 해당 타입의 원소를 관리하는 것을 강제한다.
선언하는 방식으로는 크게 두가지가 있다.
const array:number[] = [1,2,3];
const array:Array<number> = [1,2,3];
만약 여러 타입을 모두 관리해야 하는 배열을 선언하고 싶은 경우에는 유니온 타입을 사용할 수 있다.
const array1: Array<number | string> = [1,"string"];
const array2: number[] | string[] = [1,"string"];
const array3: (number | string)[] = [1,"string"];
튜플은 배열 기능에 길이 제한까지 추가한 타입 시스템이다.
대괄호와 타입시스템을 사용하여 선언할 수 있으며 대괄호 안에 선언하는 타입의 개수가 튜플이 가질 수 있는 원소의 개수를 나타낸다.
let tuple: [number] = [1]; // O, 가능
tuple = [1,2]; // X, 불가능
tuple = [1,"string"]; // X, 불가능
let tuple2: [number,string,boolean] = [1, "hello world", true]; // O, 가능
배열은 사전에 허용하지 않은 타입이 서로 섞이는 것을 방지하여 타입 안전성을 제공한다.
튜플은 여기에 길이까지 제한하여 원소의 개수와 타입을 보장한다.
이처럼 타입을 제한하는 것은 자바스크립트의 런타임 에러와 유지 보수의 어려움을 막기 위한 것이며 특히 튜플의 경우 컨벤션을 잘 지키고 각 배열 원소의 명확한 의미와 쓰임을 보장할 때 더욱 안전하게 사용할 수 있다.
enum은 열거형이라고 부르는데 일종의 구조체를 만드는 타입 시스템이다. enum을 사용하여 열거형을 정의할 수 있으며 각각의 멤버를 가진다. 이때 타입스크립트는 명명한 각 멤버의 값을 스스로 추론하고 기본적인 추론방식은 숫자 0부터 1씩 늘려가며 값을 할당하는 것이다.
enum ProgrammingLanguage {
Typescript, // 0
Javascript, // 1
Java, // 2
Python, // 3
Rust // 4
}
// ProgrammingLanguage의 멤버로 접근
ProgrammingLanguage.Typescript; // 0
ProgrammingLanguage.Javascript; // 1
ProgrammingLanguage.Python; // 3
ProgrammingLanguage["Rust"]; // 4
// 역방향 접근도 가능
ProgrammingLanguage[2]; // Java
명시적인 값의 할당도 가능하며 일부 멤버에 값을 할당하지 않아도 누락된 멤버를 아래와 같은 방식으로 이전 멤버를 기준으로 1씩 늘려가며 자동으로 할당한다.
enum ProgrammingLanguage {
Typescript = "TypeScript",
Javascript = "JavaScript",
Java = 300,
Python = 400,
Rust, // 401
Go // 402
}
enum은 주로 문자열 상수를 생성하는데 사용되며 이를 통해 응집력 있는 집합 구조체를 만들어 사용자 입장에서 간편하게 활용할 수 있다.
또한 그 자체로 변수 타입으로 지정할 수 있으며 열거형을 타입으로 가지는 변수는 해당 열거형이 가지는 모든 멤버들을 값으로 받을 수 있다.
enum ItemStatusType {
DELIVERY_HOLD = 'DELIVERY_HOLD',
DELIVERY_READY = 'DELIVERY_READY',
DELIVERING = 'DELIVERING',
DELIVERED = 'DELIVERED',
}
const checkItemAvailable = (itemStatus: ItemStatusType) => {
switch (itemStatus) {
case ItemStatusType.DELIVERY_HOLD:
case ItemStatusType.DELIVERY_READY:
case ItemStatusType.DELIVERING:
return false;
case ItemStatusType.DELIVERED:
default:
return true;
}
};
위와 같이 itemStatus라는 인자가 ItemStatusType라는 열거형을 타입으로 가지면 문자열로 타입이 지정되었을때와 비교하여 다음과 같은 효과가 있다.
타입안전성: ItemStatusType에 명시되지 않은 다른 문자열은 인자로 받을 수 없어 타입 안전성이 우수하다.
명확한 의미 전달과 높은 응집력: ItemStatusType이 다루는 값이 무엇인지 명확하고 아이템 상태에 대한 값을 모아놓은 것으로 응집력이 우수하다.
가독성: 응집도가 높기 때문에 말하고자 하는 바가 더욱 명확하다. 열거형 멤버를 통해 어떤 상태를 나타내는지 쉽게 알 수 있다.
이처럼 열거형은 관련이 높은 멤버를 모아 문자열 상수처럼 사용하고자 할 때 유용하게 사용할 수 있다.
의도하지 않은 값의 할당이나 접근
숫자로만 이루어져 있거나 타입스크립트가 자동으로 추론한 열거형은 안전하지 않은 결과를 낳을 수 있다. ex ) 할당된 값을 넘어서는 범위에 역방향으로 접근 가능
이러한 접근을 막기 위해 cosnt enum으로 열거형을 선언하는 방법이 있다. 이 방식은 역방향으로의 접근을 허용하지 않는다.
다만, const enum으로 열거형을 선언하더라도 숫자 상수로 관리되는 열거형은 선언한 값 이외의 값을 할당하거나 접근할 때 막지 못한다. 반면 문자열 상수로 관리되는 열거형은 의도하지 않은 값의 할당이나 접근을 방지하므로 문자열 상수로 관리하는 것이 도움이 된다. → 확인이 필요한 부분
불필요한 코드의 크기 증가
열거형은 타입공간과 값 공간에 모두 사용된다. 열거형은 TS에서 JS로 변환되며 즉시실행함수 형식으로 변환된다.
이때 즉시 실행 함수로 변환된 값을 사용하지 않는 코드로 인식하지 못하는 경우가 발생하여 불필요한 코드의 크기가 증가하는 결과를 초래할 수 있다.
이러한 문제를 해결하기 위해 앞서 언급한 cosnt enum 또는 as cosnt assertion을 사용하여 유니온 타입으로 열거형과 동일한 효과를 얻는 방법이 있다.