打字稿索引类型组成
Typescript index type composition
我正在尝试这样输入我的事件 API:
type Apple = {
seeds: number;
}
type Peach = {
weight: number;
}
interface AddAppleEvent {
apple: Apple;
}
interface AddPeachEvent {
peach: Peach;
}
interface EventMap {
"addApple": AddAppleEvent;
"addPeach": AddPeachEvent;
}
class EventApi{
on<K extends keyof EventMap>(type: K, listener: (ev: EventMap[K]) => any): void{
}
}
const api = new EventApi();
api.on("addApple", (evt) => {
console.log(evt.apple);
});
这很奇怪:Typescript 从我注册的事件名称中知道我的 evt 变量中有什么。
但是:我希望能够注册到一个事件数组,然后键入结果(这将是一个每个事件类型的组成):
api.on(["addApple", "addPeach"], ({apple, peach}) => {
if(apple) {
// do something
}
if(peach) {
// do something else
}
})
不幸的是,我找不到一种方法来从接口迭代键数组来构建所有引用值的联合。
有什么想法吗?
Here is a link 使用上面的示例进入 typescript playground
-----编辑----
只是总结我得到的很好的答案 post:
- 如果你想要联合:checkout this playground@t-j-crowder
- 如果你想要路口:通过@jcalz
结帐this playground
非常感谢大家:)
我首先定义 EventMapKey
只是为了避免重新输入 keyof EventMap
:
type EventMapKey = keyof EventMap;
那要看你要并集还是交集了
联盟
您可以这样定义 onMultiple
:
onMultiple<K extends EventMapKey[], EventType extends EventMap[K[number]]>(type: K, listener: (ev: EventType) => any): void {
console.log("on add multiple event listenner", listener);
}
Playground link - 我添加了第三种事件类型(只是为了确定),最后 onMultiple
的示例用法有效:
api.onMultiple(["addApple", "addPeach"], (evt) => {
// Here, evt is AddAppleEvent | AddPeachEvent
});
当然,在您可以使用 evt
的 apple
或 peach
属性之前,您必须执行类型保护以检查您正在处理的事件类型。为此,我可能会在事件类型中添加一个 type
,这样您就可以使用它:
interface AddAppleEvent {
type: "addApple";
apple: Apple;
}
interface AddPeachEvent {
type: "addPeach";
peach: Peach;
}
api.onMultiple(["addApple", "addPeach"], (evt) => {
if (evt.type === "addApple") {
console.log(evt.apple.seeds);
} else {
console.log(evt.peach.weight);
}
});
但如果您愿意,您也可以从 "apple" in evt
开始工作而不添加 type
。
api.onMultiple(["addApple", "addPeach"], (evt) => {
if ("apple" in evt) {
console.log(evt.apple.seeds);
} else {
console.log(evt.peach.weight);
}
});
路口
如果你想要一个交叉路口,这里是解决方案 :
type ComposedEventListener<K extends EventMapKey[]> = {
[I in keyof K]: (ev: EventMap[Extract<K[I], EventMapKey>]) => any
}[number] extends (ev: infer I) => any ? (ev: I) => any : never
class EventApi {
on<K extends EventMapKey>(type: K, listener: (ev: EventMap[K]) => any): void {
console.log("on add event listenner", listener);
}
// here, write K as a set of keys from EventMap allows to pass multiple keys,
// but I cant find how to type the listener properlly.
onMultiple<K extends EventMapKey[]>(type: readonly [...K], listener: ComposedEventListener<K>): void {
console.log("on add multiple event listenner", listener);
}
}
const api = new EventApi();
api.on("addApple", (evt) => {
console.log(evt.apple);
});
api.onMultiple(["addApple", "addPeach"], (evt) => { });
我正在尝试这样输入我的事件 API:
type Apple = {
seeds: number;
}
type Peach = {
weight: number;
}
interface AddAppleEvent {
apple: Apple;
}
interface AddPeachEvent {
peach: Peach;
}
interface EventMap {
"addApple": AddAppleEvent;
"addPeach": AddPeachEvent;
}
class EventApi{
on<K extends keyof EventMap>(type: K, listener: (ev: EventMap[K]) => any): void{
}
}
const api = new EventApi();
api.on("addApple", (evt) => {
console.log(evt.apple);
});
这很奇怪:Typescript 从我注册的事件名称中知道我的 evt 变量中有什么。
但是:我希望能够注册到一个事件数组,然后键入结果(这将是一个每个事件类型的组成):
api.on(["addApple", "addPeach"], ({apple, peach}) => {
if(apple) {
// do something
}
if(peach) {
// do something else
}
})
不幸的是,我找不到一种方法来从接口迭代键数组来构建所有引用值的联合。
有什么想法吗?
Here is a link 使用上面的示例进入 typescript playground
-----编辑----
只是总结我得到的很好的答案 post:
- 如果你想要联合:checkout this playground@t-j-crowder
- 如果你想要路口:通过@jcalz 结帐this playground
非常感谢大家:)
我首先定义 EventMapKey
只是为了避免重新输入 keyof EventMap
:
type EventMapKey = keyof EventMap;
那要看你要并集还是交集了
联盟
您可以这样定义 onMultiple
:
onMultiple<K extends EventMapKey[], EventType extends EventMap[K[number]]>(type: K, listener: (ev: EventType) => any): void {
console.log("on add multiple event listenner", listener);
}
Playground link - 我添加了第三种事件类型(只是为了确定),最后 onMultiple
的示例用法有效:
api.onMultiple(["addApple", "addPeach"], (evt) => {
// Here, evt is AddAppleEvent | AddPeachEvent
});
当然,在您可以使用 evt
的 apple
或 peach
属性之前,您必须执行类型保护以检查您正在处理的事件类型。为此,我可能会在事件类型中添加一个 type
,这样您就可以使用它:
interface AddAppleEvent {
type: "addApple";
apple: Apple;
}
interface AddPeachEvent {
type: "addPeach";
peach: Peach;
}
api.onMultiple(["addApple", "addPeach"], (evt) => {
if (evt.type === "addApple") {
console.log(evt.apple.seeds);
} else {
console.log(evt.peach.weight);
}
});
但如果您愿意,您也可以从 "apple" in evt
开始工作而不添加 type
。
api.onMultiple(["addApple", "addPeach"], (evt) => {
if ("apple" in evt) {
console.log(evt.apple.seeds);
} else {
console.log(evt.peach.weight);
}
});
路口
如果你想要一个交叉路口,这里是解决方案
type ComposedEventListener<K extends EventMapKey[]> = {
[I in keyof K]: (ev: EventMap[Extract<K[I], EventMapKey>]) => any
}[number] extends (ev: infer I) => any ? (ev: I) => any : never
class EventApi {
on<K extends EventMapKey>(type: K, listener: (ev: EventMap[K]) => any): void {
console.log("on add event listenner", listener);
}
// here, write K as a set of keys from EventMap allows to pass multiple keys,
// but I cant find how to type the listener properlly.
onMultiple<K extends EventMapKey[]>(type: readonly [...K], listener: ComposedEventListener<K>): void {
console.log("on add multiple event listenner", listener);
}
}
const api = new EventApi();
api.on("addApple", (evt) => {
console.log(evt.apple);
});
api.onMultiple(["addApple", "addPeach"], (evt) => { });