如何定义TypeScript类型,排除不含score属性的Model接口
问题描述
我正在开发一个前端应用,后端会生成供前端用于类型定义、服务调用等的SDK。前端使用的一个核心对象在类型定义上遇到了问题,以下是SDK生成的简化示例类型:
export enum ModelTypes { ModelOne = 'ModelOne', ModelTwo = 'ModelTwo' } export interface ModelOne { score: ModelOneScores; metadata: ModelOneMetadata; modelOneProp_A: string; modelOneProp_B: string; } export interface ModelTwo { modelTwoProp_A: number; modelTwoProp_B: number; } export interface Model { id: string; created: Date; updated: Date; data: (ModelOne | ModelTwo); type: ModelTypes; }
如你所见,ModelOne拥有score和metadata属性,而ModelTwo没有。我编写了一个从任意Model中获取分数的函数(可假设ModelOneScores或其他ModelNScores接口内包含averageScore属性):
export function getModelScore(model: Sdk.Model): number { switch (model.type) { case Sdk.ModelTypes.ModelOne: return model.data.score.averageScore; } }
显然TypeScript会报错,因为score不存在于ModelTwo。我需要一个通用类型来告诉TypeScript:我绝不会传入不含score属性的Model(比如ModelTwo),因此Sdk.Model的data始终包含score属性。
解决方案
1. 自定义筛选类型
利用TypeScript的Extract工具类型和条件判断,直接筛选出所有data包含score属性的Model子类型:
// 筛选出data带score属性的Model类型 type ModelWithScore = Extract<Model, { data: { score: unknown } }>;
这个类型会自动排除所有data中没有score的Model(比如示例中的ModelTwo对应的Model实例),只保留符合要求的类型。
2. 修改函数参数类型
将函数的参数类型替换为ModelWithScore,TypeScript就能准确推断出model.data一定包含score属性,不会再报错:
export function getModelScore(model: ModelWithScore): number { return model.data.score.averageScore; }
如果需要保留switch分支用于后续扩展(比如新增其他带score的Model类型),TypeScript也能正确识别每个分支下的data类型:
export function getModelScore(model: ModelWithScore): number { switch (model.type) { case ModelTypes.ModelOne: return model.data.score.averageScore; // 后续新增ModelThree(带score)时,直接添加case即可,TypeScript会自动推断类型 } }
3. 通用类型工具(可选)
如果需要复用这种“筛选带有指定属性的Model”的逻辑,可以封装一个通用类型工具:
// 通用类型:筛选出data包含指定属性的Model type ModelWithDataProperty<T extends keyof any> = Extract<Model, { data: Record<T, unknown> }>; // 使用示例:筛选带score的Model type ModelWithScore = ModelWithDataProperty<'score'>; // 筛选带metadata的Model type ModelWithMetadata = ModelWithDataProperty<'metadata'>;
内容的提问来源于stack exchange,提问作者exk0730
相关产品推荐
相关产品推荐

