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

如何通过映射而非switch实现TypeScript可辨识联合类型收窄

TypeScript可辨识联合类型:无需switch的类型收窄方案

需求与常规实现

我需要基于数据的type属性渲染React组件,希望在处理TypeScript可辨识联合类型时,避免使用switch/if-else语句。常规写法中,switch可以让TypeScript自动完成类型收窄:

type AView = {type: 'A', name: string} 
type BView = {type: 'B', count: number} 
type View = AView | BView

const prints =  {
    'A': (view: AView) => `this is A: ${view.name}`,
    'B': (view: BView) => `this is B. Count: ${view.count}`,
} as const

const outputWorks = (view: View): string => {
  switch (view.type) {
    case 'A':
      return prints[view.type](view)
    case 'B':
      return prints[view.type](view)
  }
}

outputWorks({type: 'A', name: 'John'})

问题:直接使用映射对象报错

我尝试省去switch,直接通过映射对象调用对应函数,但TypeScript会报错:The intersection 'AView & BView' was reduced to 'never' because property 'type' has conflicting types in some constituents.,代码如下:

type AView = {type: 'A', name: string} 
type BView = {type: 'B', count: number} 
type View = AView | BView

const prints =  {
    'A': (view: AView) => `this is A: ${view.name}`,
    'B': (view: BView) => `this is B. Count: ${view.count}`,
} as const

const outputFail = (view: View): string => prints[view.type](view)

outputFail({type: 'A', name: 'John'})

原因是TypeScript无法自动关联view.type与映射函数的参数类型,它会错误地认为函数需要接受所有联合类型的交集,而AView和BView的type属性冲突,交集为never。

解决方案:通过类型映射与泛型实现类型安全

我们可以通过类型映射明确每个type对应的View类型,再配合泛型函数让TypeScript正确关联类型:

type AView = { type: 'A', name: string }
type BView = { type: 'B', count: number }
type View = AView | BView

// 定义映射类型,确保每个type对应正确的View子类型
type ViewHandlerMap = {
  [T in View['type']]: (view: Extract<View, { type: T }>) => string
}

// 用映射类型约束prints对象,无需手动指定参数类型
const prints: ViewHandlerMap = {
  'A': (view) => `this is A: ${view.name}`,
  'B': (view) => `this is B. Count: ${view.count}`,
}

// 泛型函数让TypeScript自动关联view的type与对应的处理函数
const outputSuccess = <T extends View['type']>(view: Extract<View, { type: T }>): string => {
  return prints[view.type](view)
}

// 正常工作,类型完全安全
outputSuccess({ type: 'A', name: 'John' })
outputSuccess({ type: 'B', count: 123 })

另一种简化写法:类型断言(不推荐,但更简洁)

如果追求代码简洁,也可以在调用时用类型断言告诉TypeScript当前view的类型与函数参数匹配:

const outputSuccess = (view: View): string => {
  return prints[view.type](view as never)
}

不过这种方法跳过了部分类型检查,不如类型映射的方式严谨,适合简单场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 02:58:28