我在Select
接口中定义了一个新的签名。签名告诉TypeScript类型检查对象是否具有该函数接口的键。
import { Knex, knex } from "knex"
type AnyToUnknown<T> = unknown extends T ? unknown : T;
type ArrayIfAlready<T1, T2> = AnyToUnknown<T1> extends any[] ? T2[] : T2;
declare module 'knex/types/index' {
export namespace Knex {
interface Select<TRecord extends {} = any, TResult = unknown[]>
extends Knex.AliasQueryBuilder<TRecord, TResult>,
Knex.ColumnNameQueryBuilder<TRecord, TResult> {
<
TResult2 = ArrayIfAlready<TResult, any>,
>(
obj: { [K in keyof TResult2]: string }
): Knex.QueryBuilder<TRecord, TResult2>;
}
}
}
export interface User {
id: number;
name: string;
age: number;
}
const db = knex({});
const users = await db('user').select<User>({ id: 'id', name: 'id' }); // TypeScript should throw error: age is missing.
// ^? let users: User
const users2 = await db('user').select<User>({}).first(); // TypeScript should throw error here: id, name, age is missing.
// ^? let users: User | undefined
TypeScript应该会在这些例子中抛出错误,因为我传递给select
函数的对象没有User
接口的所有键。
为什么会这样?我的输入中缺少了什么?
如果我更改为以下代码,它就可以工作:
...
<
TResult2 = ArrayIfAlready<TResult, any>,
>(
obj: boolean
): Knex.QueryBuilder<TRecord, TResult2>;
....
const users2 = await db('user').select<User>(true).first(); // accept boolean, without it typescript throw error.
Playground
1条答案
按热度按时间eagi6jfj1#
你面临的问题似乎源于条件类型ArrayIfAlready。AnyToUnknown类型似乎没有提供所需的行为,允许未知类型通过。让我们重写ArrayIfAlready类型以删除对AnyToUnknown的需要:
type ArrayIfAlready〈T1,T2〉= T1 extends any[]?T2[]:T2;
然而,这仍然不会抛出所需的错误。这是因为TResult默认为unknown[],而TResult 2默认为any。由于未知类型是所有类型的超类型,因此即使您没有提供User接口的所有键,它也被认为是有效的。
要解决这个问题,您可以修改Select接口以要求TResult的默认类型:
interface选择〈TRecord extends {} = any,TResult = ArrayIfAlready〈TRecord,any〉〉