NodeJS 为什么TypeScript不会为这个新签名抛出错误?

hlswsv35  于 2023-04-20  发布在  Node.js
关注(0)|答案(1)|浏览(124)

我在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

eagi6jfj

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〉〉

相关问题