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

TypeScript中如何严格校验可选属性,避免非法类型赋值?

解决TypeScript联合类型接受额外属性的问题

问题根源

TypeScript采用结构类型系统——只要对象满足目标类型的所有必填属性,就算携带额外属性也会被判定为类型兼容。直接赋值字面量时会触发额外属性检查,但如果变量先被标注为更宽泛的类型(比如示例中的c)再赋值,该检查就不会生效,最终导致bar能被赋值给a | b类型的foo,但实际包含服务器不接受的c属性。

可行解决方案

1. 给联合类型成员添加禁止额外属性的约束

修改a和b类型,通过never类型明确禁止c属性存在:

type a = {a : number, b: number, c?: never}; // 不允许存在c属性
type b = {a: number, b: number, id: number, c?: never}; // 不允许存在c属性

type c = {a: number, b: number, c?: number};
const bar: c = {a: 12, b: 10, c: 15};

const foo: a | b = bar; // 现在会触发编译错误

never类型表示该属性不能有任何有效值,只要对象携带c属性(哪怕是可选状态),就无法通过类型校验。

2. 使用类型守卫做严格校验

编写类型守卫函数,既检查对象是否符合a | b的结构要求,又过滤掉携带c属性的对象:

type a = {a : number, b: number};
type b = {a: number, b: number, id: number};
type c = {a: number, b: number, c?: number};

function isAOrB(obj: unknown): obj is a | b {
  if (typeof obj !== 'object' || obj === null) return false;
  const target = obj as Record<string, unknown>;
  
  // 先校验必填属性
  if (typeof target.a !== 'number' || typeof target.b !== 'number') return false;
  // 禁止存在c属性
  if (target.c !== undefined) return false;
  // 校验b类型的可选id属性
  if (target.id !== undefined) return typeof target.id === 'number';
  
  return true;
}

const bar: c = {a: 12, b: 10, c: 15};

if (isAOrB(bar)) {
  const foo: a | b = bar; // 仅通过校验的对象能进入此分支
} else {
  // 自动剔除c属性,生成符合要求的对象
  const { c, ...cleanedBar } = bar;
  const foo: a | b = cleanedBar;
}

3. 手动清理对象多余属性

在发送给服务器前,直接剔除多余的c属性,确保对象结构完全符合要求:

type a = {a : number, b: number};
type b = {a: number, b: number, id: number};
type c = {a: number, b: number, c?: number};

const bar: c = {a: 12, b: 10, c: 15};

// 通过解构剔除c属性,得到结构干净的对象
const { c, ...cleanedBar } = bar;
const foo: a | b = cleanedBar;

// 发送请求时使用清理后的对象
fetch('/api/your-endpoint', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(cleanedBar)
});

4. 用类型断言配合Omit移除多余属性

如果需要强制转换类型,可以结合Omit类型先剔除c属性,再做类型断言:

type a = {a : number, b: number};
type b = {a: number, b: number, id: number};
type c = {a: number, b: number, c?: number};

const bar: c = {a: 12, b: 10, c: 15};

// 移除c属性后断言为a | b类型
const foo: a | b = bar as Omit<c, 'c'>;

补充说明

此问题属于TypeScript结构类型系统的设计特性,目前官方相关讨论尚未有最终解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:01:31