[TypeScript] Promise.all에는 인자 개수 제한이 없다
트위터에서 이상한 글을 봤다. TypeScript가 선언한 Promise.all
이 iterable
을 받지 않고 인자 개수 제한이 있다는 것이다. 근데 그럴 리가. TypeScript쯤 되는 프로젝트에서 무언가가 스펙과 다른 것으로 보인다면, 코드를 잘못 이해했을 확률이 높다. 코드를 다시 확인해보자.
당연히 iterable
을 인자로 받는 Promise.all
선언도 있다. 다만, 이 선언이 es2015.promise.d.ts
가 아닌 es2015.iterable.d.ts
에 있을 뿐이다. 타입스크립트 컴파일러 옵션 중 --lib 으로 사용할 라이브러리를 지정할 수 있다. 대부분 es2015
, es2018
같은 식으로 버전을 지정하여 포함하는 것이 일반적이라 잘 알려지지 않았지만, es2015.symbol
로 Symbol
에 대한 선언만 포함하거나, es2015.promise
로 Promise
에 대한 선언만 포함하는 것이 가능하다. 각 라이브러리는 서로 간에 의존성이 없도록 작성돼 있으며, 특히 iterable
에 관련된 선언은 전부 es2015.iterable
에서 선언한다. 예를 들어 Map
이나 Set
은 es2015.collection
에 선언돼 있지만, iterable
을 반환하는 entries
, values
, keys
등의 선언은 전부 es2015.iterable
에 선언돼 있다. 마찬가지로 iterable
을 인자로 받는 Promise.all
함수도 es2015.iterable에 선언돼있다.
iterable
을 받는 Promise.all
함수 선언은 es2015.iterable
에 선언돼 있는데, es2015.promise
에 10개가 더 선언돼있다. 이들은 무엇일까? 사실 이 함수들은 배열을 인자로 받는 함수가 아니라 tuple을 인자로 받는 함수 선언이다. JavaScript에는 임의 개수의 원소를 가지는 배열은 있지만, 고정된 개수의 원소를 가지는 tuple은 없다. 그래서 tuple이 필요할 때 배열을 사용한다. 이를 TypeScript에서는 별도의 타입으로 표시할 수 있도록 했다. T
라는 타입의 배열은 T[]
로 표시하고, T1
, T2
, T3
라는 타입의 원소 3개를 가지는 tuple은 [T1, T2, T3]
로 표현하는 식이다. 즉, es2015.iterable
에는 iterable
을 인자로 받는 Promise.all
을 선언하고, es2015.promise
에는 tuple을 인자로 받는 함수를 선언한 것이다.
그렇다면 왜 길이가 11개 이상인 tuple을 인자로 받고 싶을 때는 어떻게 해야 할까? 11개 짜리 tuple을 받는 함수는 선언되지 않았기 때문에 iterable로 해석하여 결과 값을 tuple이 아닌 배열로 돌려준다. 반환되는 배열의 타입은 tuple 각 원소의 union type이다. 다시 말해서 타입 정보를 잃는다. 강제로 타입 변환을 하거나 타입 가드를 사용하면 되지만 이는 코드를 verbose하게 만든다.
그럴 때는 그냥 11개 이상인 tuple을 인자로 받는 함수를 선언해서 사용하면 된다. Promise
의 전역 함수 all
을 선언하는 것이 아닌 PromiseConstructor
의 함수 all
을 선언하는 것만 기억하면 된다. Promise.race
도 같은 방식으로 확장할 수 있다.
하지만 사용할 수 있는 tuple의 크기를 확장하기 전에 한 번 더 고민해봐야 한다. tuple은 각 원소가 무엇을 의미하는지 코드에 명시적으로 드러나지 않고 숨겨져 있다. 이런 암시적인 코드는 수정 시 놓치기 쉽고, 버그의 원인이 된다. 개인적으로는 10개짜리 tuple도 크다고 생각한다. tuple의 크기는 3개만 넘어가도 다른 표현법이 없는지 찾아봐야 한다. 하지만 절대적인 것은 아니다. tuple로밖에 처리하지 못할 때에는 새 선언을 이용해 함수를 확장해서 사용하면 된다.
좋은 글 써주셔서 감사합니다. :) 저도 오해하도록 글을 두서 없이 썼네요. 좋은 정보 알려주셔서 감사합니다.
답글삭제