You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript如何将含可空类型的元组转换为非可空元组?

实现memoize函数的非可空元组类型推导问题

我要实现一个memoize函数用于缓存传入函数的返回值,当前代码如下:

const memoize = (value, onHandle) => {
  for (let index = 0; index < value.length; index++) {
    if (value[index] === null || value[index] === undefined) {
      return
    }
  }
  // 检查是否存在相同参数的缓存值
  return onHandle(value)
}

回调函数onHandle接收非可空值元组,返回将被缓存的值。我需要声明一个类型工具,把包含可空类型的元组转换为仅含非可空类型的元组,示例如下:

type Tuple = [number | undefined, string, boolean, string | undefined]
type StrictTuple = [number, string, boolean, string]

我尝试了多次都没成功,以下是失败的尝试代码:

尝试1:错误保留原可空类型

type StrictTuple<T extends [unknown, ...unknown[]]> = T extends [
  unknown,
  ...unknown[]
]
  ? T[number] extends null | undefined
    ? never
    : T[number] extends infer U
    ? U extends null | undefined
      ? never
      : T
    : never
  : never;
type Example1 = StrictTuple<[1 | undefined, 2, "3", true | null, boolean]> 
// 结果:[1 | undefined, 2, "3", true | null, boolean](未移除可空类型)

尝试2:错误将元组转为同类型数组的联合

type StrictTuple<T extends [E, ...E[]], E = unknown> = T[number] extends infer U ? U extends null | undefined ? never : [U, ...U[]]: never
type Example2 = StrictTuple<[1, 2, "3", boolean, number | undefined]>
// 结果:[number, ...number[]] | [false, ...false[]] | [true, ...true[]] | ["3", ..."3"[]](不符合元组结构)

请问有没有方法生成仅含非可空值的元组类型?


解决方案:递归遍历元组元素移除可空类型

可以通过递归遍历元组的每个元素,逐个移除null和undefined类型,同时保留元组的结构:

type NonNullableTuple<T extends readonly unknown[]> = 
  T extends [infer First, ...infer Rest]
    ? [NonNullable<First>, ...NonNullableTuple<Rest>]
    : [];

// 测试示例
type Tuple = [number | undefined, string, boolean, string | undefined]
type StrictTuple = NonNullableTuple<Tuple>; 
// 结果:[number, string, boolean, string](符合预期)

type Example1 = NonNullableTuple<[1 | undefined, 2, "3", true | null, boolean]>;
// 结果:[1, 2, "3", true, boolean](正确移除可空类型)

给memoize函数添加完整类型声明

结合上面的类型工具,给memoize函数加上类型,确保只有当传入的元组所有元素非空时,才会调用onHandle:

type NonNullableTuple<T extends readonly unknown[]> = 
  T extends [infer First, ...infer Rest]
    ? [NonNullable<First>, ...NonNullableTuple<Rest>]
    : [];

const memoize = <T extends readonly unknown[]>(
  value: T,
  onHandle: (args: NonNullableTuple<T>) => unknown
): unknown | undefined => {
  for (let index = 0; index < value.length; index++) {
    if (value[index] === null || value[index] === undefined) {
      return
    }
  }
  // 类型断言:此时value的所有元素都非空,符合NonNullableTuple<T>
  return onHandle(value as NonNullableTuple<T>)
}

// 测试使用
memoize([1, "test", true], (args) => {
  // args类型为[number, string, boolean]
  console.log(args[0].toFixed(2)); // 正常调用number方法
})

memoize([1, undefined, true], (args) => {
  // 这里不会触发,因为函数会提前return
})

内容的提问来源于stack exchange,提问作者nw.lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:17:39