如何让TypeScript函数的idKey与idValue参数类型关联?
编译阶段确保idKey与idValue类型一致的TypeScript解决方案
原函数代码
/** * 检查字符串类型的值是否已存在于数据数组中 * @param data 数据数组 * @param key 要检查的字符串属性键 * @param value 要检查的字符串值 * @param idKey 用于排除自身条目的id属性键(可选) * @param idValue 对应idKey的属性值(可选) * @returns 如果值存在于数组中则返回true */ function checkIfStringIsDuplicated<K extends PropertyKey, H extends PropertyKey = never>( data: Array<Partial<Record<K, string | null>> & Partial<Record<NoInfer<H>, string | number | null>>>, key: K, value: string, idKey?: H, idValue?: string | number | null ): boolean { return !!data.find((item) => { // 如果在数据数组中找到匹配的值 if (item[key] && item[key]?.trim().toLowerCase() === value.trim().toLowerCase()) { // 检查该条目的id是否与传入的idValue匹配 // 如果匹配,说明是当前正在编辑的条目,不算重复 // 如果不匹配,则判定为重复 if (idKey && item[idKey] && idValue) { if (typeof item[idKey] !== typeof idValue) throw new Error( `DuplicateValidationService: ${item[idKey]}的类型(${typeof item[idKey]})与${idValue}的类型(${typeof idValue})不匹配` ); return !(item[idKey] === idValue); } else { // 如果未提供idKey或条目无ID,则判定为重复 return true; } } else { return false; } }); }
基础用法示例
interface Car { id: number; name: string; } const dataArray: Array<Car> = [{id: 123, name: 'Tesla'}] // 此处传入字符串'123'与id的number类型不匹配,目前仅能在运行时报错 console.log(checkIfStringIsDuplicated(dataArray, 'name', 'Tesla', 'id', '123'))
问题描述
该函数用于检查对象数组中是否存在指定字符串属性值重复的条目;若传入idKey和idValue,会忽略对应id的条目(适用于编辑场景)。当前存在的问题是:idKey与idValue的类型可能不匹配,目前仅能在运行时抛出错误提示,无法在编译阶段提前发现问题。
解决方案:通过泛型关联类型
我们可以调整泛型参数,让idValue的类型严格对应idKey在数据项中的属性类型,从而在编译阶段强制两者类型一致。修改后的函数如下:
/** * 检查字符串类型的值是否已存在于数据数组中 * @param data 数据数组 * @param key 要检查的字符串属性键 * @param value 要检查的字符串值 * @param idKey 用于排除自身条目的id属性键(可选) * @param idValue 对应idKey的属性值(可选) * @returns 如果值存在于数组中则返回true */ function checkIfStringIsDuplicated<T extends object, K extends keyof T>( data: T[], key: K, value: string, idKey?: keyof T, idValue?: idKey extends keyof T ? T[idKey] : never ): boolean { return !!data.find((item) => { // 确保要检查的属性是字符串类型(编译阶段约束) const itemValue = item[key]; if (typeof itemValue !== 'string') return false; if (itemValue.trim().toLowerCase() === value.trim().toLowerCase()) { if (idKey !== undefined && idValue !== undefined) { // 此处类型已通过泛型关联,无需运行时类型检查 return item[idKey] !== idValue; } else { return true; } } else { return false; } }); }
关键改进点
- 泛型参数调整:使用
T表示数据项的完整类型,K约束为T的键,确保key是数据项中存在的属性。 - 类型关联:通过条件类型
idKey extends keyof T ? T[idKey] : never,让idValue的类型严格对应idKey在T中的属性类型。 - 编译阶段约束:新增对
item[key]的类型检查,确保该属性是字符串类型,避免非字符串属性传入。
测试验证
interface Car { id: number; name: string; } const dataArray: Array<Car> = [{id: 123, name: 'Tesla'}] // ✅ 正确用法:idValue为number类型,与id的类型匹配 console.log(checkIfStringIsDuplicated(dataArray, 'name', 'Tesla', 'id', 123)); // ❌ 编译阶段报错:类型'string'不能赋值给类型'number' console.log(checkIfStringIsDuplicated(dataArray, 'name', 'Tesla', 'id', '123'));
这样就可以在编译阶段直接拦截idKey与idValue类型不匹配的问题,无需等到运行时才发现错误。
内容的提问来源于stack exchange,提问作者Konstantin Schütte
相关产品推荐
相关产品推荐

