如何用TypeScript第一个参数约束后两个参数的计算属性名类型?
实现基于属性名约束的可复用includes函数
我想要做一个可复用的includes函数,要求第一个参数传属性名,后面的列表和值参数必须包含这个属性。最开始的代码是这样的:
type Primitive = string | boolean | number; export const includesByAttribute = ( attr: string, list: { [attr]: Primitive }[], val: { [attr]: Primitive }, ) => { if (list.length > 0) return list.find((obj) => obj[attr] === val[attr]); return false; };
后来改了写法,但还是没实现约束效果:
export const includesByAttribute = ( attr: string, list: { [attr: string]: Primitive }[], val: { [attr: string]: Primitive }, ) => { if (list.length > 0) return list.find((obj) => obj[attr] === val[attr]); return false; };
比如调用时传了不匹配的属性名,代码居然还能正常编译,完全不符合预期:
includesByAttribute('value', [{ x: 1 }], { x: 2 }) // 可编译但不符合预期
要让第一个参数的属性名能约束后两个参数的类型,得用TypeScript的泛型来捕获属性名字面量类型,这样列表和值的类型就能跟着这个属性名走。
基础实现版本
type Primitive = string | boolean | number; export const includesByAttribute = <K extends string>( attr: K, list: Record<K, Primitive>[], val: Record<K, Primitive>, ) => { return list.length > 0 ? list.find(obj => obj[attr] === val[attr]) : false; };
这么写之后:
- 泛型
K会抓牢你传入的具体属性名(比如'value') Record<K, Primitive>强制要求列表里的每个对象和值对象都必须有K这个属性,而且属性值得是Primitive类型- 再调用
includesByAttribute('value', [{ x: 1 }], { x: 2 })就会直接报编译错误,完全符合预期
更灵活的版本(允许对象带额外属性)
如果需要列表里的对象可以有其他多余属性,只约束指定属性的存在性,可以改成这样:
type Primitive = string | boolean | number; export const includesByAttribute = <K extends string, T extends Record<K, Primitive>>( attr: K, list: T[], val: Pick<T, K>, ) => { return list.length > 0 ? list.find(obj => obj[attr] === val[attr]) : false; };
这个版本的好处是:
- 泛型
T表示列表里对象的具体类型,只要它包含K属性就行,其他属性随便加 Pick<T, K>允许值对象只带T类型里的K属性,不用完全跟列表对象的结构一致
内容的提问来源于stack exchange,提问作者Marc Sloth Eastman
相关产品推荐
相关产品推荐

