我有以下 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({})
因上述代码中的类型错误而失败?我想定义一种记录类型,它至少定义了一个记录。
答案 0 :(得分:1)
首先,您需要去掉索引签名,因为索引签名会影响类型的形状:
keyof { [x:string]: any }; // string | number
Credit 用于通用实用程序类型转到 Mihail,我仅将其调整为 TS 4.1+。其要点是,如果 string
是 keyof 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