typescript - 用打字稿重载类型
问题描述
我有以下实现
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
:
但是当您开始编写对象时,它会丢失轨道并跳转到第二个重载:
我能做些什么来帮助它吗?
解决方案
我认为您的问题是,一旦您键入{}
,那是一个与类型不匹配的空对象,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 & S
which is just S
。但如果S
is 本身可分配给keyof T
,S & 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:
希望这对你有用,或者至少给你一些想法。祝你好运!