从元组类型中删除所有可选项目

Remove all optional items from a Tuple type

假设我想将一个包含可选项(如 [1, 2, 3?, 4?])的元组转换为仅包含必需项的数组 -> [1, 2]

我想出的如下所示,它永远不会变成所有可选项目,我被困在这里。

type OnlyReq <L extends any []> = {
  [K in keyof L]-?: L [K] extends Required <L> [K]  ? L [K] : never
}

type Found = OnlyReq <[1, 2, 3?, 4?]> // [1, 2, never, never]

playground

我这里的做法是写一个recursive conditional type (actually tail recursive so it will work for quite long tuples in TS4.5+) that walks through the tuple,直到它发现剩下的部分都是可选的

注意optional elements in tuple types后面不能跟必需的元素;也就是说,像 [1, 2?, 3] 这样的东西是不可能的。因此,如果元组具有 any 个必需元素,则必须特别需要第一个元素。

这是一个实现:

type OnlyReq<T extends any[], U extends any[] = []> = Partial<T> extends T ? U :
  T extends [infer F, ...infer R] ? OnlyReq<R, [...U, F]> : U

我们在类型参数 U 中累积结果(它以空元组 [] 开始),所以一旦我们决定停止迭代,我们 return U.

检查 Partial<T> extends T 使用 the Partial<T> utility type 得出输入元组的全可选版本。一般来说 T extends Partial<T> 是正确的,但 Partial<T> extends T 不是,除非 T 已经与 Partial<T> 相同...换句话说,Partial<T> extends T 当且仅当T 是全选的。

如果元组 T 是全可选的,那么我们 return U。此外,如果 T 为空,我们 return U (如果 T 不能拆分为第一个元素 F 和其余元组 R).如果 T 有第一个元素 F,那么我们知道它是必需的(否则 T 将是全可选的),我们可以将它推到 [=16= 的末尾] 元组的其余部分的递归调用 R.


让我们看看它是否有效:

type Found = OnlyReq<[1, 2, 3?, 4?]>
// type Found = [1, 2]

看起来不错。

另请注意,rest elements in tuple types 在同一测试中也被视为“可选”,因此也应将其删除:

type StripRest = OnlyReq<[string, boolean?, ...number[]]> 
// type StripRest = [string]

事实上,非元组数组类型 (Foo[]) 等同于仅由剩余元素 ([...Foo[]]) 组成的元组,因此将被转换为一个空元组,它可能或可能不是你想要的:

type Hmm = OnlyReq<number[]>
// type Hmm = []

我认为 "leading" or "middle" rest elements in tuple types 超出了这里的范围,因为它们会做一些奇怪的事情而你没有问过它们(我希望它不会出现,因为操纵这样的东西很烦人类型):

type What = OnlyReq<[...string[], number]>
// type What = []
type AlsoWhat = OnlyReq<[string, ...boolean[], number]>
//type AlsoWhat = [string]

Playground link to code