[Typescript] Array와 Tuple (Tuple에서 왜 push, pop 등이 가능한가?)

갱갱·2024년 2월 8일
0

Typescript

목록 보기
3/4
post-thumbnail

✨ Array와 Tuple


Array는 다른 언어에서도 많이 나오는 개념이고 당연히 자바스크립트에도 존재하기 때문에 이해하기 어렵지 않을 것이다. 타입스크립트에는 이런 배열 형태를 조금 더 특수하게 사용할 수 있는 tuple이라는 타입이 있다. 자바스크립트에서는 지원되지 않는 데이터 타입이다.
파이썬에도 튜플이 있기는 하다. 요솟값을 생성, 수정, 삭제를 할 수 있는 리스트와는 다르게 튜플은 요솟값을 바꿀 수 없다. 이 파이썬의 튜플과 타입스크립트의 튜플이 같다고 할 수는 없지만 비슷한 점은 존재한다. 타입스크렙트의 튜플에서는 타입에 맞게 요솟값을 수정을 할 수는 있지만 마찬가지로 생성이나 삭제는 불가능하다(라고 생각했는데...). 이후에 설명하겠지만 튜플은 타입의 순서도 정해져 있고 고정된 크기를 가지고 있기 때문이다.

💫 Array


타입스크립트에서 배열을 생성하는 방법은 간단하다.

let numbers: number[] = [1, 2, 3, 4, 5];
let fruits: string[] = ['apple', 'banana', 'strawberry'];

기존 타입을 지정했던 방식과 동일하게 타입을 선언하되, 그 뒤에 배열을 생성하는 대괄호를 추가해주면 된다. 일단은 기본적으로 하나의 타입으로만 배열을 생성하였지만, 지난번에 배웠던 유니온 타입을 이용해서 여러 타입을 선언할 수도 있다.

let mixedArray: (string | number)[] = ['apple', 3, 4, 'banana'];

만약 배열을 순회하며 타입에 따라 다른 동작을 수행하고 싶게 하고 싶다면 타입가드를 사용하자.

Array는 동적인 크기를 가지고 있기 때문에 삽입, 수정, 삭제 등이 자유롭다. 아래 예시 사진을 한 번 보자.

위처럼 배열의 정렬이나 삽입, 수정 등의 메서드를 자유롭게 사용할 수 있다.

💫 Tuple


튜플은 동적인 크기를 가지는 배열과는 다르게 고정된 크기를 가진다. 또한 서로 다른 타입을 가질 수 있다. 사용 방법은 아래와 같다.

let tuple: [string, number] = ['apple', 3];
let tuple2: [number, string, boolean] = ['apple', 3, true]; // Error

고정된 크기를 가지고 있고, 타입의 순서가 정해져 있기 때문에 해당하는 순서에 맞지 않는 값이 오거나 다른 크기의 요소가 할당될 수 없다.
그래서 당연히 요솟값을 직접적으로 삽입, 삭제하는 push나 pop 등이 오지 못할 거라고 생각을 했는데...

🚨 Tuple에서 push, pop 등이 사용되는 이유


당연히 되지 않을 거라고 생각하고 코드를 작성한 후 실행을 했다. 근데 내 예상과는 전혀 다른 결과가 나왔다.

...? 왜 되지? 튜플은 고정된 크기를 가지고 있고 마찬가지로 타입의 순서 또한 정해져 있는데 그렇다면 push나 pop 등 요솟값에 직접적인 영향을 주는 메서드를 사용할 수 없어야 하는 것 아닌가?

실제 바나나를 기존 튜플에 추가해보면 위와 같은 에러가 뜬다.

왜 이러는 거지...? 예상과 다른 결과에 나는 당황해서 열심히 구글링을 하기 시작했다. 그리고 아니나 다를까, 나와 비슷한 생각을 한 사람들이 이미 스택오버플로에 질문글을 많이 올려두셨다.

[스택오버플로] Typescript array push method can't catch a tuple type of the array

나와 아주 똑같은 생각을 하신 분이다. 1번은 작동을 안 하는데 왜 2번은 작동이 되는가? 이런 비슷한 질문이 상당히 많이 올라와 있었다.


이게 답변인데... 그냥 요약하자면 이미 논의가 된 문제이지만 마이크로소프트에서 거부함 이거다. 기존 코드에 변화가 너무 크게 생길 것 같아서라고 한다.

[Github Issue] Remove destructive methods in tuple type

실제로 깃허브 이슈에 이런 제안을 한 사람이 있다. push, pop, shift 같은 건 제한해야 하는 것 아닌가요? 답변을 봐도 그냥 다른 방법을 제안할 뿐 튜플은 그냥 배열이다 우리 설계는 그렇기 때문에 바꾸지 않을 거다 뭐 이런 내용이다.

이에 따른 제안이 깃허브 이슈에 오픈되어 있는데

[Github Issue] New type for tuples which have mutable properties but can't be mutated with array methods

type StrictTuple<T extends any[]> =
    Omit<T, keyof any[]> extends infer O ? { [K in keyof O]: O[K] } : never;

보다 엄격한 관리를 위해 특정 조건을 만족하는 튜플 타입을 제안하고 있다. 실제 코드를 작성해서 확인해보았다.

드디어 원하는 결과가 반환되고 있긴 하다. 그렇긴 한데... 보니까 실제 배열로 작동하는 것이 아니라 key-value 쌍의 객체 타입을 만드니까 push를 사용하지 못하는 것 같다. 그래서 스택오버플로에 답변을 다신 분도 근데 오히려 이렇게 하는 게 더 번거로울 수도 있으니 그냥 튜플 변형하지 말고 사용하세요~ 라고 하고 계신다. 보니까 이건 최근까지도 논의되고 있는 부분인 것 같아 뭐랄까 속시원히 결론이 나지 않았다.

🍀 결론


우선 Array와 Tuple의 차이점에 대해 마지막으로 정리해보자.

  1. Array
    : 배열은 동적인 크기를 가진다. 모든 요소가 동일한 타입이어야 한다. (유니온 타입 같은 경우는 다른 타입이 올 수도 있음) 요소의 추가외 제거 등이 자유롭다.
  2. Tuple
    : 튜플은 고정된 크기를 가진다. 각 요소가 다른 타입일 수도 있으며 타입의 순서가 정해져있다. 고정된 크기를 가지고 있으니 요소의 추가, 제거가 자유롭지 않지만... 딱히 제한을 하고 있지는 않다. (계속 논의 중인 사항인 듯)
profile
괜찮은 개발자가 되어 보자

0개의 댓글