TS+vue3中,如何根据参数来给一个对象设置类型?

场景是这样的:
一个活动详情页面,有如下通用参数:id,title,imageUrl,description
于是我定义了一个通用接口:

interface ActivityDetailBase {
  id: number
  title: string
  imageUrl: string
  description: string
}

同时,现在有两个活动,A活动、B活动,都有上述那些参数。但也有些区别,A活动详情里面有price,totalLimit;B活动详情有beginTime,endTime。于是我分别定义了两个接口:

interface ADetail extends ActivityDetailBase {
  price: number
  totalLimit: string
}
interface BDetail extends ActivityDetailBase {
  beginTime: number
  endTime: number
}

现在进入详情页时在页面路径后拼接参数type=A或type=B,详情页中定义的活动详情对象:

const activityDetail = reactive({
  data: <ADetail | BDetail>{}
})

想知道,怎么根据type的值来动态设置activityDetail的类型?
如果type=A,则activityDetail的类型则为 ADetail
如果type=B,则activityDetail的类型则为 BDetail

如果要在页面上显示price,则需要在模板中这样写,感觉比较麻烦
<div>{{ (activityDetail.data as ADetail).price}}</div>
如果想取 beginTime,endTime的值:就得用as语法来断言
function test() {
 const { beginTime, endTime } = activityDetail.data as BDetail
// ......
}
阅读 3.8k
2 个回答

通常来说的话是:

type MyType = 
    { type: 'A', foo: number } 
  | { type: 'B', bar: string }

然后 TS 会自动推断:

let obj: MyType;
if (obj.type === 'A') {
   console.log(obj.foo); // ok
   console.log(obj.bar); // error!
} else if (obj.type === 'B') {
   console.log(obj.foo); // error!
   console.log(obj.bar); // ok
}

不过这只限于在 TS 中,你这里似乎是需要通过 vue 的 v-if 来实现相关判断,那在模板里还是得类型断言,这么做意义就不大了。

除了 @IanSun 提到的拆成两个组件以外,如果不同类型下页面本身整体变化并不大,只是控制某些活动部分显示或隐藏,你也可以考虑声明类型时都放在一起就好:

// 前提是没有类型冲突的情况存在,比如在 A 中这个字段是这种类型、在 B 中同名的字段是另一种类型
interface ActivityDetailBase {
  id: number
  title: string
  imageUrl: string
  description: string
  price?: number
  totalLimit?: string
  beginTime?: number
  endTime?: number
}

模板里该 v-if 判断就继续判断,然后后面正常取值 + 非空断言就可以了,不需要类型断言。

因为其实在后端吧这玩意儿大概率就是同一张数据库表、同一个实体模型,后端本身就是定义在一起的没做区分。

不太了解你详细的代码。
但理解下来应该是,为什么不是对ActivityDetailBase单独写一个组件,然后再分别写2个高阶组件对应ADetailBDetail呢?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题