如何设计TypeScript接口约束仅单个属性类型不同的两类?
在TypeScript中实现数据库实体与JSON约束类的类型复用
这个场景在实际开发里太常见了——数据库实体关联的是完整的外键对象,但对外返回的JSON只需要存关联ID字符串,既要保证类型安全又不想重复写属性对吧?我给你几个实用的解决方案:
方案一:基础接口扩展法
先定义所有公共属性的基础接口,再分别扩展出数据库实体和JSON约束的接口,重写catalogue属性的类型即可,简单直观:
// 定义数据库中的CatObj外键实体类型 type CatObj = { id: string; name: string; // 这里可以添加CatObj的其他属性,比如createTime等 }; // 公共属性的基础接口:包含除catalogue外的所有字段 interface BaseItem { id: string; title: string; description: string; // 其他共用属性... } // 数据库实体类接口:catalogue为完整的CatObj对象 interface DBItem extends BaseItem { catalogue: CatObj; } // JSON约束类接口:catalogue为字符串类型(对应CatObj的id) interface JSONItem extends BaseItem { catalogue: string; }
方案二:泛型接口法
如果以后catalogue的类型可能有更多变化(比如有时候需要number类型的ID),用泛型来封装会更灵活,一次定义适配多种场景:
type CatObj = { id: string; name: string; }; // 泛型接口,用CatalogueType参数指定catalogue的类型 interface Item<CatalogueType> { id: string; title: string; description: string; catalogue: CatalogueType; } // 数据库实体类型:传入CatObj作为泛型参数 type DBItem = Item<CatObj>; // JSON约束类型:传入string作为泛型参数 type JSONItem = Item<string>; // 如果需要其他类型,比如number类型的ID,直接扩展即可: type JSONItemWithNumberId = Item<number>;
额外补充:类型转换与类型守卫
实际开发中经常需要在两种类型之间转换,或者判断当前类型是哪一种,这里给你几个实用的工具函数:
类型转换函数
// 将DBItem转换为JSONItem:提取CatObj的id作为catalogue字符串 function dbItemToJsonItem(dbItem: DBItem): JSONItem { return { ...dbItem, catalogue: dbItem.catalogue.id }; } // 将JSONItem转换为DBItem:需要根据ID获取对应的CatObj(比如从数据库查询) async function jsonItemToDbItem( jsonItem: JSONItem, fetchCatObj: (id: string) => Promise<CatObj> ): Promise<DBItem> { const catalogue = await fetchCatObj(jsonItem.catalogue); return { ...jsonItem, catalogue }; }
类型守卫函数
用来判断一个对象是DBItem还是JSONItem,确保类型安全:
function isDBItem(item: DBItem | JSONItem): item is DBItem { // 通过判断catalogue的类型来区分 return typeof (item as DBItem).catalogue !== 'string'; } // 使用示例 function processItem(item: DBItem | JSONItem) { if (isDBItem(item)) { console.log('处理数据库实体:分类名称是', item.catalogue.name); } else { console.log('处理JSON数据:分类ID是', item.catalogue); } }
内容的提问来源于stack exchange,提问作者Li Jinyao
相关产品推荐
相关产品推荐

