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

如何为带可选/可空参数的函数进行正确TypeScript类型标注?

解决TypeScript中可空参数函数的输入输出类型关联问题

你提到的问题本质是输入输出类型关联性缺失:当函数接收number | null | undefined联合类型时,原签名无法让TypeScript推断出「输入非空则输出非空,输入为空则输出同类型空值」的对应关系。而函数重载的局限性在于无法直接处理联合类型参数,这时候可以用泛型条件类型来实现需求,这其实就是Maybe monad在TypeScript类型系统中的落地实现。

实现方案

// 定义可选值类型别名,对应Maybe monad的容器类型
type Maybe<T> = T | null | undefined;

function addOne<T extends Maybe<number>>(num: T = null as T): T extends number ? number : T {
  if (num === null || typeof num === "undefined") {
    return num as T;
  }
  return (num + 1) as T extends number ? number : T;
}

方案说明

  1. 泛型约束:T extends Maybe<number>确保输入只能是number、null或undefined及其联合类型
  2. 条件返回类型:T extends number ? number : T实现了类型映射逻辑:
    • 如果输入T是number,返回number
    • 如果输入T是null或undefined,返回原类型
  3. 默认值兼容:null as T解决了泛型参数下默认值的类型兼容性问题

验证各种场景

1. 确定非空输入

const num1: number = 5;
const res1 = addOne(num1); // res1 类型为 number

2. 确定空值输入

const num2: null = null;
const res2 = addOne(num2); // res2 类型为 null

const num3: undefined = undefined;
const res3 = addOne(num3); // res3 类型为 undefined

3. 联合类型输入

let num4: number | null = Math.random() > 0.5 ? 5 : null;
const res4 = addOne(num4); // res4 类型为 number | null

4. 类型窄化后的调用(你的核心痛点场景)

let num5: number | null = Math.random() > 0.5 ? 5 : null;
if (num5 !== null && typeof num5 !== "undefined") {
  num5 = addOne(num5); // 此处num5仍被推断为number,完全符合预期
}

为什么这个方案优于重载

  • 直接支持联合类型参数传入,无需拆分不同类型的调用分支
  • 类型系统自动维护输入输出的关联关系,不需要手动维护多个重载签名
  • 完全符合TypeScript文档「优先使用联合类型而非重载」的设计原则

与Monad的关联

这个实现对应了函数式编程中的Maybe Monad:

  • Maybe<T>是承载可选值的容器
  • addOne是对容器内值的映射函数(map操作),当容器为空时直接返回空容器,非空时处理内部值并返回新的非空容器,完美保留了可选值的上下文语义。

内容的提问来源于stack exchange,提问作者dingus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:51:07