typescript - 具有未知参数的函数不能分配给具有字符串参数的函数
问题描述
我正在使用打字稿,我定义了以下接口:
export interface BaseProtocol {
[key: string]: (payload: unknown) => Promise<unknown>;
}
现在我想定义一个协议:
import { BaseProtocol } from './BaseProtocol';
export interface AppProtocol extends BaseProtocol {
action(payload: string): Promise<string>;
}
不幸的是,我收到以下错误:
Property 'action' of type '(payload: string) => Promise<string>' is not assignable to string index type '(payload: unknown) => Promise<unknown>'.ts(2411)
我不愿意使用任何,因为它损害了协议使用的类型安全。知道如何解决这个问题吗?
解决方案
该错误是有效的;如果你有接口A
和B extends A
,那么如果我想要一个A
,你可以给我一个B
。但是根据您的定义,这会中断:
function callBaseProtocol(bp: BaseProtocol) {
return bp.action(12345);
}
function callAppProtocol(ap: AppProtocol) {
callBaseProtocol(ap); // oops!
}
该函数callBaseProtocol()
没有错误。的定义BaseProtocol
说每个字符串值的键都是一个函数,它接受一个类型的值unknown
并返回一个Promise<unknown>
. 具体来说, a 的action
属性BaseProtocol
必须是一个接受 type 值的函数unknown
。因此,您可以将其传递给number
.
该功能callAppProtocol()
也没有错误。在这里,我们调用callBaseProtocol()
并传递一个AppProtocol
. 这应该是可以接受的,因为AppProtocol extends BaseProtocol
.
但是,如果 aAppProtocol
有一个action
期望 a 的属性string
并在其上调用一些string
特定的方法,例如x => x.toUpperCase()
,这将爆炸。错误在于AppProtocol
实际上没有正确扩展BaseProtocol
.
这种可能令人惊讶的现象被称为参数逆变,如果X
是 的子类型Y
,则函数(y: Y)=>any
是 的子类型(x: X)=>any
,反之则不然。子类型化的方向对于函数参数是相反的,而“相反”就是“逆变”的意思。
这种行为是通过--strictFunctionTypes
编译器选项在 TypeScript 中引入的,是一种类型安全性改进。
那么你怎么能处理这个呢?假设您想要提高类型安全性而不是降低类型安全性(您提到不想使用any
),那么您BaseProtocol
的定义可能并不正确。unknowns
也许它应该是一个通用接口,而不是一个字符串索引签名和传递,它表示输入和输出之间更复杂的关系。也许是这样的:
export type BaseProtocol<T> = {
[K in keyof T]: (payload: T[K]) => Promise<T[K]>;
}
这就是说 aBaseProtocol
依赖于另一种类型T
,并且 aBaseProtocol<T>
对 的每个属性都有一个方法T
,该方法接受该属性值类型的参数,并返回Promise
该类型的 a。
然后AppProtocol
可以简单定义为:
export interface AppProtocol extends BaseProtocol<{ action: string }> {
}
这解决了之前的问题,因为编译器不允许您在参数上调用 aBaseProtocol<T>
的action()
方法,number
除非它知道它T
具有action
type 的属性number
:
function callBaseProtocol<T>(bp: BaseProtocol<T>) {
return bp.action(12345); // error! might not have an action
}
还有一种混合方法,它不假设每个函数的返回值是与其输入相同类型的承诺:
export type BaseProtocol<T> = {
[K in keyof T]: (payload: T[K]) => unknown;
}
export interface AppProtocol extends BaseProtocol<{ action: string }> {
action(x: string): Promise<string>;
}
好的,希望有帮助;祝你好运!
推荐阅读
- java - Netty 服务器中 Dropwizard 指标和 JMeter 之间的延迟值不一致
- php - 获取类别中显示的所有产品的 WooCommerce 产品条款
- php - Phalcon PHP 单元测试 DI 共享实例
- java - 如何在 Java 中使用 JAXB 将 XML 字符串解组到列表
- google-apps-script - 如何使用 Google Apps 脚本编辑 Google 照片中的多张照片?
- timestamp - 从 Notepad++ 中的字幕文件中删除空行和时间戳
- android - Android /获取与被解雇的通知相对应的ID
- dart - 我尝试创建 facebook 登录。如何解决这个问题?
- c# - 我们能否在具有共享托管的 Linux 服务器上托管一个自包含的 ASP.NET Core 应用程序
- extjs - 表单字段值在提交过程中转义,除非表单字段值禁用选项