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

如何强制方法参数为无属性超类型的子类型?

如何强制函数参数仅属于无属性超类型的子类型

问题背景

我们有一系列用于更新操作的类型,示例如下:

type DateUpdateObj = { newDate: Date }
type LengthUpdateObj = { newLength: number, unit: 'm'|'cm' }
// 后续可扩展更多同类型的 ...UpdateObj 结构

需要实现一个函数,仅接受上述这类更新子类型作为参数,拒绝其他任意类型。

首次尝试的问题

参考Java/Kotlin的标记接口思路,尝试用私有属性类作为标记:

class UpdateObj { private readonly _: never } // 作为标记接口
type MyUpdateObj = { foo: string } & UpdateObj  // 新的更新对象类型

function send<T extends UpdateObj>(updateObj: T) {} // 仅接受UpdateObj类型

测试时出现不符合预期的情况:

send("") // 符合预期,编译失败
send(new Date())  // 符合预期,编译失败
send({ foo: 'hurray' } as MyUpdateObj) // 符合预期,编译通过

const myUpdate: MyUpdateObj = { foo: "err" } // 预期编译通过但实际失败,因缺少私有属性_,且类型断言会破坏源码兼容性
send(myUpdate)

可行解决方案:品牌类型标记

利用TypeScript的unique symbol实现品牌类型,既不需要添加实际运行时属性,又能在编译时完成类型约束:

// 定义全局唯一的品牌标记符号
const UpdateBrand: unique symbol = Symbol('update-brand');

// 标记接口,仅包含可选的品牌属性(无需实例化时添加)
interface UpdateObj {
  [UpdateBrand]?: never;
}

// 所有更新类型交叉该标记接口
type DateUpdateObj = { newDate: Date } & UpdateObj;
type LengthUpdateObj = { newLength: number, unit: 'm'|'cm' } & UpdateObj;
type MyUpdateObj = { foo: string } & UpdateObj;

// 函数约束为仅接受带标记的类型
function send<T extends UpdateObj>(updateObj: T) {}

测试验证

send("") // 编译失败,符合预期
send(new Date()) // 编译失败,符合预期
const myUpdate: MyUpdateObj = { foo: "success" } // 编译通过,无需额外属性
send(myUpdate) // 编译通过,符合预期

方案说明

  • unique symbol确保标记唯一,避免其他类型意外满足约束
  • 可选的品牌属性[UpdateBrand]?: never,让类型实例化时无需添加任何额外属性,仅在编译时做类型校验
  • 所有需要被允许的更新类型只需交叉UpdateObj,就能被send函数接受,其他未标记类型会被编译拦截

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 11:20:54