typescript - 泛型函数的对象
问题描述
我试图让这个打字稿工作,但我不知道如何键入openPromises
对象以使resolve
函数没有错误,因为它输入错误。我看到的错误是:
Type '(value?: B | PromiseLike<B> | undefined) => void' is not assignable to type '<C>(value?: C | undefined) => void'.
Types of parameters 'value' and 'value' are incompatible.
Type 'C | undefined' is not assignable to type 'B | PromiseLike<B> | undefined'.
Type 'C' is not assignable to type 'B | PromiseLike<B> | undefined'.
Type 'C' is not assignable to type 'PromiseLike<B>'.
一个简单的解决方法是键入openPromises
as的值any
,但希望在此基础上进行改进并在此过程中了解更多关于复杂泛型类型的信息。
这是我的代码,它证明了我的问题。我的来源要复杂得多,但这是我可以创建的证明问题的最低限度
interface DefaultResponse {
a: 'a'
}
const openPromises: {
[id: string]: { resolve<C>(value?: C): void }
} = {};
function putInObject<B extends DefaultResponse>(id: string) {
return new Promise<B>(resolve => {
openPromises[id] = { resolve };
})
}
// ----------
interface MyResponse1 extends DefaultResponse {
AA: string;
}
putInObject<MyResponse1>("test1").then(console.log);
const response1: MyResponse1 = { a:'a', AA: "test" };
openPromises["test1"].resolve(response1)
interface MyResponse2 extends DefaultResponse {
BB: string;
}
putInObject<MyResponse2>("test2").then(console.log);
const response2: MyResponse2 = { a:'a', BB: "test" };
openPromises["test2"].resolve(response2)
您可以在此打字稿代码沙箱的左侧看到错误。谢谢你的帮助!
解决方案
问题是您尝试做的并不是真正的类型安全。泛型函数是具有由调用者决定的类型参数的函数。在您的情况下,调用者只能传入一个有效的类型,即在putInObject
为特定 ID 调用时确定的类型。因此,例如,这是允许的,即使它无效:
putInObject<MyResponse1>("test1").then(r => console.log(r.AA)); // expecting r to be MyResponse1
openPromises["test1"].resolve({ a:'a', BB: "test" }) // I can pas in anything since resolve is generic
没有一种openPromises
真正类型安全的预先输入的好方法。由于每个键只有一个有效类型,并且这些键是稍后添加的,因此我们不能真正使用类型来openPromise
模拟这种行为。
我能想到两种解决方法。
改变原始对象的类型
Typescript 不允许我们在声明变量后更改它的类型,但是我们会从putInObject
相同的对象返回但类型不同,然后使用这个新对象来代替。下面,我使用了一个类来保持当前的类型openPromisses
class OpenPromisesManager<T = {}> {
readonly openPromises: T = {} as T;
putInObject<B extends DefaultResponse>(){
return <TKey extends string>(id: TKey, withPromise: (p:Promise<B>) => void): OpenPromisesManager<T & Record<TKey, { resolve(value?: B): void }>> => {
const newThis = this as any ;
withPromise(new Promise<B>(resolve => {
newThis.openPromises[id] = { resolve };
}));
return newThis as OpenPromisesManager<T & Record<TKey, { resolve(value?: B): void }>>;
}
}
}
// ----------
interface MyResponse1 extends DefaultResponse {
AA: string;
}
const mgr = new OpenPromisesManager()
const mgr2 = mgr.putInObject<MyResponse1>()("test1", p=> p.then(r=> console.log(r.AA)));
const response1: MyResponse1 = { a:'a', AA: "test" };
mgr2.openPromises["test1"].resolve(response1)
mgr2.openPromises["test1"].resolve({ a:'a', BB: "test" }) // error
使用捕获请求类型的键
另一种选择是拥有一个在其类型中保持承诺的预期返回类型的键。这允许我们创建一个get
函数,该函数将返回一个具有适当resolve
类型的对象。
const openPromises: unknown = {};
type Key<T> = string & { __type: T }
function key<T>(k: string) {
return k as Key<T>;
}
function putInObject<B extends DefaultResponse>(id: Key<B>) {
return new Promise<B>(resolve => {
(openPromises as any)[id] = { resolve };
})
}
function get<B>(id: Key<B>): { resolve(value?: B): void } {
return (openPromises as any)[id];
}
// ----------
interface MyResponse1 extends DefaultResponse {
AA: string;
}
const test1 = key<MyResponse1>("test1");
putInObject<MyResponse1>(test1).then(console.log);
const response1: MyResponse1 = { a:'a', AA: "test" };
get(test1).resolve(response1)
get(test1).resolve({ a: 'a', BB: ''}) // error
推荐阅读
- javascript - Firebase Cloud Messaging : Request is missing required Authentication Credentials
- c++ - 错误:bool' 不能用 'explicit' 说明符声明
- reactjs - ReactJs:如何将数据从控制台打印到网页?
- javascript - 除了警报之外,我还能用什么来告诉用户十六进制代码已被复制?
- pdf - 使用 iText7 提取 pdf 文件中包含的签名图像
- assembly - 用于虚拟到物理地址转换的 FreeBSD 模块
- microsoft-graph-api - 从 Azure 数据工厂管道调用 MS Graph API
- node.js - 如何处理停止发送数据的 HTTP 请求
- ruby-on-rails - 如何在 RoR 中锁定记录
- python - 试图弄清楚如何传递函数,以便它可以显示值