TypeScript如何实现ReadonlyArray<IProduct>的id属性唯一且不引入额外框架
解决方案
因为数组仅会一次性创建、后续不会修改,不需要额外引入第三方框架,以下是两种适配不同场景的原生实现方案:
方案1:运行时校验/去重(适用动态生成的产品列表)
通过Set记录已出现的id,在生成只读数组阶段完成去重,也可以根据需求遇到重复id直接抛出错误提前终止:
interface IProduct { id: number; name: string; price: number; description?: string; } function createUniqueProductArray(products: IProduct[]): ReadonlyArray<IProduct> { const existedIds = new Set<number>(); const uniqueProducts: IProduct[] = []; for (const product of products) { if (existedIds.has(product.id)) { // 严格校验场景下直接抛错,不需要去重可开启下行注释 // throw new Error(`产品id重复: ${product.id}`); continue; } existedIds.add(product.id); uniqueProducts.push(product); } // 冻结数组保证完全只读,无法后续增删改 return Object.freeze(uniqueProducts); } // 使用示例 const readonlyProducts = createUniqueProductArray([ { id: 1, name: "笔记本", price: 5999 }, { id: 2, name: "手机", price: 3999 }, { id: 1, name: "重复的笔记本", price: 5899 } // 该条会被自动过滤 ]);
方案2:编译期静态校验(适用代码内写死的常量列表)
如果你的产品数组是直接硬编码在代码里的常量,可以用TS类型实现编译期的id重复校验,不需要等到运行时就能发现问题:
interface IProduct { id: number; name: string; price: number; description?: string; } // 类型层面判断id是否重复 type CheckDuplicateId<T extends IProduct[], UsedIds = never> = T extends [infer First extends IProduct, ...infer Rest extends IProduct[]] ? First["id"] extends UsedIds ? `重复的产品id: ${First["id"]}` : CheckDuplicateId<Rest, UsedIds | First["id"]> : never; // 静态常量定义工具 function defineConstProductList< T extends readonly IProduct[] >(list: CheckDuplicateId<[...T]> extends never ? T : CheckDuplicateId<[...T]>): ReadonlyArray<IProduct> { return Object.freeze(list); } // 无重复时编译正常 const validList = defineConstProductList([ { id: 1, name: "键盘", price: 299 }, { id: 2, name: "鼠标", price: 199 }, ] as const); // 有id重复时编译直接报错,提示「不能将类型“"重复的产品id: 1"”分配给类型“never”」 const invalidList = defineConstProductList([ { id: 1, name: "键盘", price: 299 }, { id: 1, name: "重复键盘", price: 279 }, ] as const);
内容的提问来源于stack exchange,提问作者Szwarceneger16
相关产品推荐
相关产品推荐

