繁体   English   中英

推断 typescript 中的映射参数类型

[英]Infer a mapped parameter type in typescript

我正在使用一个具有有趣类型声明(简化)的库:

class Emitter {
    public on<EventName extends 'streamCreated' | 'streamDestroyed'> (eventName: EventName, callback: (event: {
        streamCreated: {id: number};
        streamDestroyed: {reason: string};
    }[EventName]) => void): void;
}

我正在尝试在我提供的回调中键入事件:

const callback = (event: StreamDestroyedEvent) => console.log(event.reason);
emitter.on('streamDestroyed', callback);

但是“StreamDestroyedEvent”不存在。 它不是由库提供的,而是仅存在于该匿名事件映射中,因此我尝试推断它:

type InferCallback<T, E> = T extends (eventName: E, event: infer R) => void ? R : never;
type InferCallbackEvent<T, E> = InferCallback<T, E> extends (event: infer P) => void ? P : never;
type StreamDestroyedEvent = InferCallbackEvent<Emitter['on'], 'streamDestroyed'>;

但是,它没有给我一个{reason: string}类型,而是得到联合类型{reason: string;} | {id: number;} {reason: string;} | {id: number;} 我怎样才能得到正确的类型,或者这是否与我将得到的一样接近?

您基本上想为EventName类型参数“插入”一个类型。 虽然 TypeScript 在调用泛型 function 时支持这一点,但它并不能很好地在类型系统本身中表示此类类型。 这样做完全需要支持更高级别的类型,这并不是语言的一部分。 emitter.on function 本身并不能真正让您“部分地”调用它,以便您使用第一个参数插入EventName ,然后让编译器告诉第二个参数的类型应该是什么。

在 TypeScript 3.4 出来之前,我可能会说从编译器中获取这些信息是不可能的。 我会尝试类似的东西

emitter.on("streamDestroyed", x => {
  type StreamDestroyedEvent = typeof x; // can't get it out of the scope though!
})

然后无法获取StreamDestroyedEvent类型以逃避 function。

TypeScript 3.4 引入了改进的泛型函数支持高阶类型推断 你仍然不能纯粹在类型系统中表示你在做什么,但现在我们可以定义一个 function ,它为我们提供了一些我们可以做的“部分”调用并保留我们需要的类型信息。

Here is a currying function which takes a multi-argument function and returns a new function of one argument that returns another function of the remaining arguments:

const curry = <T, U extends any[], R>(
  cb: (t: T, ...args: U) => R
) => (t: T) => (...args: U) => cb(t, ...args);

如果您在emitter.on上调用curry ,然后使用"streamDestroyed"调用,(在 TS3.4+ 中)您会得到一个 function ,它需要一个回调,该回调需要一个StreamDestroyedEvent ,您现在可以捕获:

const streamDestroyedFunc = curry(emitter.on.bind(emitter))("streamDestroyed")
type StreamDestroyedEvent = Parameters<Parameters<typeof streamDestroyedFunc>[0]>[0];
// type StreamDestroyedEvent = { reason: string; }

请注意,上面的实际运行时行为不是重点; 我试图确保它实际上会做一些合理的事情,但你也可以只使用类型断言来向编译器谎报正在发生的事情,只要你在运行它时没有遇到运行时错误:

const fakeCurry: typeof curry = () => () => null!;
const fakeOn: Emitter["on"] = null!;
const fakeFunc = fakeCurry(fakeOn)("streamDestroyed");
type SDE2 = Parameters<Parameters<typeof fakeFunc>[0]>[0]; // same thing

好的,希望有帮助; 祝你好运!

链接到代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM