You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何定义仅含单个任意属性的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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:24:16