如何定义仅含单个任意属性的TypeScript对象类型?
实现仅接受单键对象的TypeScript类型
需求说明
需要定义一个TypeScript类型SingleKeyObject,仅允许包含单个任意键的对象赋值:
- 包含1个属性的对象正常通过类型检查
- 空对象或包含2个及以上属性的对象直接触发类型报错
示例代码:
const A: SingleKeyObject = {name: "John"} // 合法 const B: SingleKeyObject = {department: "Sales", age: 30} // 报错:属性过多 const C: SingleKeyObject = {department: "Sales"} // 合法 const D: SingleKeyObject = {} // 报错:属性过少
同时需要将该类型应用到自定义的Store类的bind方法中,限制回调函数只能返回单键对象,类似do notation风格但无需单独传入键名:
type Dict = Record<string, any>; type SingleKeyObject<T extends Dict> = T; // 示例占位类型,需替换为实际实现 class Store<T extends Dict> { constructor(public values: T) {} bind = <U extends Dict>(bindfn: (values: T) => SingleKeyObject<U>) => { const values = { ...this.values, ...bindfn(this.values) }; return new Store(values); }; } const store1 = new Store({a: 1, b: 2}) const store2 = store1.bind(({a, b}) => ({c: a + b})) // 应允许 const store3 = store1.bind(({a, b}) => ({d: a + b, e: a * b})) // 应报错
解决方案
1. 基础单键对象类型实现
通过TypeScript条件类型结合keyof操作符,判断对象的键数量是否为1:
// 辅助类型:判断类型是否为never(用于检测多余键) type IsNever<T> = [T] extends [never] ? true : false; // 泛型版本:约束传入的T必须是单键对象 type SingleKeyObject<T extends Record<string, unknown>> = keyof T extends infer K ? K extends keyof T ? IsNever<Exclude<keyof T, K>> extends true ? T : never : never : never; // 非泛型版本:直接匹配任意单键对象 type SingleKeyObject = Record<string, unknown> & { [K in string]: unknown; } extends infer O ? O extends Record<infer Key, unknown> ? Exclude<keyof O, Key> extends never ? O : never : never : never;
2. 应用到Store类中
修改Store类的类型约束,确保bind方法的回调返回值必须是单键对象:
type Dict = Record<string, any>; // 辅助类型:判断对象是否为单键结构 type IsSingleKey<T extends Dict> = keyof T extends infer K ? K extends keyof T ? Exclude<keyof T, K> extends never ? true : false : false : false; // 约束仅接受单键对象 type SingleKeyObject<T extends Dict> = IsSingleKey<T> extends true ? T : never; class Store<T extends Dict> { constructor(public values: T) {} bind = <U extends Dict>( bindfn: (values: T) => SingleKeyObject<U> ): Store<{ ...T, ...U }> => { const newValues = { ...this.values, ...bindfn(this.values) }; return new Store(newValues); }; } // 测试案例 const store1 = new Store({a: 1, b: 2}) const store2 = store1.bind(({a, b}) => ({c: a + b})) // 正常,返回Store<{a: number; b: number; c: number;}> const store3 = store1.bind(({a, b}) => ({d: a + b, e: a * b})) // 报错:返回值不符合单键约束
类型原理说明
- 通过
keyof T获取对象的所有键集合 - 使用
Exclude<keyof T, K>排除当前键后,若结果为never,说明对象仅包含该单个键 - 若存在多个键,
Exclude会返回剩余的键名集合,此时类型返回never触发报错
内容的提问来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

