首页 > 解决方案 > 用打字稿重载类型

问题描述

我有以下实现

export class DbService<T extends object> {

    addOne<TKey extends keyof T>(table: TKey, object: T[TKey]) {
        return this.db.table(String(table)).add(object);
    }

}

当您将有效接口作为 T 时,它可以正常工作:

interface DbSchema {
    users: User
}

var db = new DbService<DbSchema>();

// working fine here.
// It asks for 'users' key that matches the DbSchema
// And asks for User object implementation
db.addOne('users', { name: 'foo' });

但是现在我希望该DbSchema接口可以是可选的,并且您可以将任何键作为表格。所以我尝试了以下重载方法:

addOne<TKey extends keyof T>(table: TKey, object: T[TKey]): T[TKey];
addOne<O extends object>(table: string, object: O): O;
addOne(table: any, object: any) {
    return this.db.table(String(table)).add(object);
}

我可以说它部分工作,但打字稿停止帮助我实现对象,即使键与DbSchema.

它开始良好,显示来自以下的键DbSchema

测试 1

但是当您开始编写对象时,它会丢失轨道并跳转到第二个重载:

测试 2

我能做些什么来帮助它吗?

标签: typescript

解决方案


我认为您的问题是,一旦您键入{},那是一个与类型不匹配的空对象,User编译器会立即切换到另一个重载,因为"users"matchesstring{}matches object

这里一种可能的解决方案是确保第二个重载接受已知密钥,如下所示:

  addOne<TKey extends keyof T>(table: TKey, object: T[TKey]): T[TKey];
  addOne<S extends string, O extends object>(table: S & Exclude<S, keyof T>, object: O): O;
  addOne(table: any, object: any) {
    return null!;
  }

现在第二个重载的table参数为 type S & Exclude<S, keyof T>,其中Exclude<A, B>是一个实用程序类型,它采用联合类型A并删除任何可分配给 的成员B。如果S是一个与 不同的字符串keyof T,那么S & Exclude<S, keyof T>将是S & Swhich is just S。但如果Sis 本身可分配给keyof TS & Exclude<S, keyof T>则将 be S & never,即never

该参数S将被推断为传入的字符串文字值 for table。这意味着只要table不在 中keyof T,则将table针对 进行检查S,这很好。但如果table在 中keyof T,则将table针对 进行检查never,这是一个错误。因此,一旦您传递了类似"users"in fortable的内容,编译器就会确定它与第二个重载不匹配。因此,当您在键入{}时,编译器没有理由切换到第二个重载,并且您获得了所需的 IntelliSense:

addOne 的良好 IntelliSense(表:

希望这对你有用,或者至少给你一些想法。祝你好运!

链接到代码


推荐阅读