undefined
가 아닌지 확인하기 위한 함수가 필요했다.??
)는 undefined
와 null
을 모두 필터링한다.Array.find
method는 올바른 타입 추론이 되지않았다.아래는 위 내용을 해결한 함수와 타입이다.
firstNotUndefined
함수는 rest parameter로 받는 요소 중 undefined가 아닌 첫번째 요소를 정확한 타입의 추론과 함께 반환한다.
const firstNotUndefined = <Tuple extends unknown[]>(
...array: Tuple
): UndefinedCoalescing<Tuple> => array.find((item) => item !== undefined);
UndefinedCoalescing
타입은 다음의 과정으로 동작한다.
type Foo = UndefinedCoalescing<[number | undefined, string | undefined]>
// type Foo = string | number | undefined
type Bar = UndefinedCoalescing<[number, string | undefined]>
// type Bar = number | string
type Baz = UndefinedCoalescing<[number, string]>
// type Baz = number | string
UndefinedCoalescing
은 Nullish Coalescing
에서 따온 이름이다. 이 유틸타입을 조금 더 자세히 들여다보자.
export type UndefinedCoalescing<Tuple extends unknown[]> = TupleToUnion<
// Tuple의 모든 요소가 undefined를 갖는다면, Tuple의 유니온 타입으로 반환
EveryHas<Tuple, undefined> extends true
? Tuple
// 그렇지 않다면 각 요소에서 undefined를 제외한 튜플의 유니온 타입으로 반환
: EveryExclude<Tuple, undefined>
>;
EveryHas는 튜플에서 모든 요소가 condition을 갖는지 재귀를 통해 확인해준다.
type EveryHas<Tuple extends unknown[], Condition> = Tuple extends [
infer FirstItem,
...infer Rest
]
? ItHas<FirstItem, Condition> extends false
? false
: EveryHas<Rest, Condition>
: true;
ItHas는 Condition이 Item에 포함되어있는지 확인해준다.
대괄호로 Condition, Item을 감싸준 이유는 타입의 분산성을 방지하여 사용하기 위함이다.
distributive-conditional-types
type ItHas<Item, Condition> = [Condition] extends [Item] ? true : false;
EveryExclude는 재귀를 통해 튜플의 각 요소에서 Condition을 제외한 타입의 튜플로 바꿔준다.
type EveryExclude<Tuple extends unknown[], Condition> = Tuple extends [
infer FirstItem,
...infer Rest
]
? [
ItHas<FirstItem, Condition> extends true
? Exclude<FirstItem, Condition>
: FirstItem,
...EveryExclude<Rest, Condition>
]
: [];
튜플의 비구조화, 재귀를 통해 타입안정성을 더한 코딩을 할수있었다.
튜플의 비구조화는 타입스크립트 4.0에 새롭게 추가되었다. 그 이전에는 좀 더 복잡한 방식으로 구현되었으며 그 내용은 참고 블로그에 자세하게 작성되었으며 꽤 재밌으니 참고.
https://blog.cometkim.kr/posts/typescript-tuples/
https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/#variadic-tuple-types
https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types