在processRequest()
的主体内,TypeScrip不能真正执行像Parameters<MyDb[K]>
这样的generic conditional types的抽象推理(因为K
或Method
是泛型类型参数,而the Parameters
utility是用条件类型实现的.这样的类型对于类型判断器来说基本上是不透明的,因此为了判断它,db[method]
的类型从泛型MyDb[K]
扩展到函数MyDb[keyof MyDb]
的union.通常,除非向其传递intersection个参数(请参见the release notes for the introduction of support for calling a union of functions),否则不能安全地调用函数的联合.基本上,db[method]
类型和data
类型之间的类属correlation已经丢失.
如果您不关心编译器理解相关性的能力,您可以只使用type assertion,然后继续您的工作:
(db[method] as (arg: typeof data) => void)(data); // okay
但是,如果您希望编译器遵循您的推理,那么推荐的方法,如第microsoft/TypeScript#47109节所述,是重构您的类型操作,以便它们以某种"基本"映射类型的形式表示,泛型indexed accesses映射到该基本类型,mapped types表示在该基本类型上.这最终将使用与您所拥有的相同的类型,但它们将以一种让编译器"看到"预期关系的方式来表示.
有一种方法可以做到这点.首先,让我们将基类型定义为Db
个方法的参数.这可以通过编程方式完成:
type DbParams = { [K in keyof MyDb]: Parameters<MyDb[K]>[0] }
/* type DbParams = {
create: {
name: string;
id: number;
};
delete: number;
read: number;
} */
然后可以写入processRequest
,这样data
就直接是DbParams[K]
:
function processRequest<K extends keyof MyDb>(
method: K,
data: DbParams[K]
) {
const _db: { [P in keyof MyDb]: (arg: DbParams[P]) => void } = db;
_db[method](data); // okay
}
重要的是,我们将类型Db
的db
赋给了映射类型{ [P in keyof MyDb]: (arg: DbParams[P]) => void }
的变量_db
.这等同于Db
,但它被显式地编写为覆盖基本DbParams
类型的映射类型.赋值之所以成功,是因为它实际上不是泛型(Db
和映射类型都是不依赖于K
的特定类型).
现在,这通电话起作用了._db[method]
的类型被明确地视为(arg: DbParams[K]) => void
.这是接受类型DbParams[P]
的参数的单个调用签名.由于这正是data
的类型,因此调用_db[method](data)
成功.
同样,此版本和您的版本之间的唯一区别是类型在泛型/映射/索引版本方面的表示形式,如微软/TypeScrip#47109中所述.如果这不是必需的,那就太好了,但是现在,如果您想要避免类型断言或类似的东西,就必须这样做.
Playground link to code个