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

TypeScript 中如何给结构相同的对象定义不同的类型?

TypeScript结构类型兼容场景下的标称类型实现优化方案

你遇到的问题根源是TypeScript默认使用结构类型系统,只要两个类型的成员结构完全一致,就会被判定为兼容类型。你需要的是标称类型的效果:即使结构一致,只要定义来源不同,就被判定为不同类型。下面提供两种可直接落地的简化实现方案:

方案1:轻量化编译期品牌类型(优先推荐,无运行时开销)

该方案仅通过编译期类型标记实现类型隔离,不需要额外的运行时代码,实现成本最低:

// 通用品牌类型工具
type Brand<T, B extends string> = T & { __brand: B }

// 定义通用Shape结构
type Shape<Data, BrandTag extends string> = Brand<{
  getArea: () => number
  data: Data
}, BrandTag>

interface CreateShapeCategoryOpts<Data, BrandTag extends string> {
  brand: BrandTag
  getArea: (data: Data) => number
}

function createShapeCategory<Data, BrandTag extends string>({ brand, getArea }: CreateShapeCategoryOpts<Data, BrandTag>) {
  const create = (data: Data): Shape<Data, BrandTag> => {
    // 不需要运行时判断可直接删除__brand赋值,用as强转即可
    return {
      getArea: () => getArea(data),
      data,
      __brand: brand
    } as Shape<Data, BrandTag>
  }
  return { create }
}

使用示例:

// 定义矩形
interface RectangleData { width: number, height: number }
const rectangle = createShapeCategory({
  brand: 'Rectangle',
  getArea: ({ width, height }: RectangleData) => width * height
})
// 直接通过工具类型推导Rectangle类型,无需手动重复声明
type Rectangle = ReturnType<typeof rectangle.create>

// 定义三角形
interface TriangleData { width: number, height: number }
const triangle = createShapeCategory({
  brand: 'Triangle',
  getArea: ({ width, height }: TriangleData) => width * height / 2
})
type Triangle = ReturnType<typeof triangle.create>

// 类型校验测试
const myRect = rectangle.create({ width: 2, height: 3 })
const myTri = triangle.create({ width: 2, height: 3 })
const rectOnlyFn = (rect: Rectangle) => {}
rectOnlyFn(myTri) // 编译报错,符合预期

方案2:内置Symbol的标称类型(适合需要运行时类型判断的场景)

如果需要运行时唯一标识、避免品牌名字符串冲突,可以将Symbol的创建封装到工厂内部,无需外部手动传入Symbol,大幅减少样板代码:

interface Shape<S extends symbol, D> {
  sentinel: S
  getArea: () => number
  data: D
}

function createShapeCategory<D>(name: string, getArea: (data: D) => number) {
  // 内部自动生成唯一Symbol,无需外部传入
  const sentinel = Symbol(name)
  const create = (data: D): Shape<typeof sentinel, D> => ({
    sentinel,
    getArea: () => getArea(data),
    data,
  })
  return { create }
}

使用示例:

// 定义矩形
interface RectangleData { width: number, height: number }
const rectangle = createShapeCategory('Rectangle', ({ width, height }: RectangleData) => width * height)
type Rectangle = ReturnType<typeof rectangle.create>

// 定义三角形
interface TriangleData { width: number, height: number }
const triangle = createShapeCategory('Triangle', ({ width, height }: TriangleData) => width * height / 2)
type Triangle = ReturnType<typeof triangle.create>

// 类型校验测试
const myRect = rectangle.create({ width: 2, height: 3 })
const myTri = triangle.create({ width: 2, height: 3 })
const rectOnlyFn = (rect: Rectangle) => {}
rectOnlyFn(myTri) // 编译报错,符合预期

方案选择建议

  • 仅需编译期类型安全,无运行时类型判断需求:选择方案1,代码量最少,无额外运行时开销。
  • 需要运行时区分实例类型、担心品牌名字符串冲突:选择方案2,相比原有实现减少70%以上的样板代码,无需外部手动创建传入Symbol。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 19:39:03