如何检查对象是否至少有一条记录?

时间:2021-06-11 15:42:03

标签: typescript

我有以下 TS 代码:

type FunctionMap = Record<
  string,
  (...params: any) => any
>

function needsARecordOfFunctions(functions: FunctionMap) {
  /* ... */
}

needsARecordOfFunctions({ myFunc: 'foobar' }); // Type 'string' is not assignable to type '(...params: any) => any'.
needsARecordOfFunctions(); // Expected 1 arguments, but got 0.
needsARecordOfFunctions({ myFunc: () => {} }); // ✅

// This passes but I want it to fail
needsARecordOfFunctions({});

我的问题是,如何让 needsARecordOfFunctions({}) 因上述代码中的类型错误而失败?我想定义一种记录类型,它至少定义了一个记录。

Playground

1 个答案:

答案 0 :(得分:1)

首先,您需要去掉索引签名,因为索引签名会影响类型的形状:

keyof { [x:string]: any }; // string | number

Credit 用于通用实用程序类型转到 Mihail,我仅将其调整为 TS 4.1+。其要点是,如果 stringkeyof T 的子类型,则意味着该类型具有 string 索引签名(检查 number 获取我们的数字签名):

type RemoveIndex<T> = {
  [ P in keyof T as string extends P ? never : number extends P ? never : P ] : T[P]
};

接下来需要让编译器推断传递给参数的参数类型进行处理。因此,您需要一个通用函数签名,在您的情况下,单个参数限制为 FunctionMap

最后,您必须确保推断的类型通过“无空对象”约束。这可以通过应用 keyof {}never 的观察来实现(有关详细信息,请参阅 this Q&A)。因此,条件 keyof RemoveIndex<T> extends never ? never : T 确保此类空对象类型不可分配(请参阅 this Q&A 以获取类似示例):

function needsARecordOfFunctions<T extends FunctionMap>(functions: keyof RemoveIndex<T> extends never ? never : T) { /* ... */ }

将所有这些结合在一起:

needsARecordOfFunctions({ myFunc: 'foobar' }); // Type 'string' is not assignable to type 'never'
needsARecordOfFunctions(); // Expected 1 arguments, but got 0.
needsARecordOfFunctions({ myFunc: () => {}, func2: () => 42 }); // ✅
needsARecordOfFunctions({}); // error, expected

Playground