在使用 Flow 扩展 EventEmitter 的 class 中限制 `eventName` 的类型?
Limiting the type of `eventName` in a class that extends EventEmitter with Flow?
举个例子,假设我有一个 class,它只发出三个可能的事件 – 'pending'
或 'success'
或 'failure'
。此外,eventHandler
中接收到的参数类型取决于发出的事件 –
- 如果
'pending'
,eventHandler
没有收到参数
- 如果
'success'
,eventHandler
收到 number
- 如果
'failure'
,eventHandler
收到 Error
以下是我尝试建模的方式:
// @flow
import EventEmitter from 'events'
type CustomEventObj = {|
pending: void,
success: number,
error: Error
|}
declare class MyEventEmitter extends EventEmitter {
on<K: $Keys<CustomEventObj>>(
eventName: K,
eventHandler: (
e: $ElementType<CustomEventObj, K>,
...args: Array<any>
) => void
): this
}
但是,这会导致如下错误:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ test.js:12:3
Cannot extend EventEmitter [1] with MyEventEmitter because an indexer property is missing in CustomEventObj [2] in the
first argument of property on.
[1] 3│ import EventEmitter from 'events'
:
8│ error: Error
9│ |}
10│
11│ declare class MyEventEmitter extends EventEmitter {
[2] 12│ on<K: $Keys<CustomEventObj>>(
13│ eventName: K,
14│ eventHandler: (
15│ e: $ElementType<CustomEventObj, K>,
16│ ...args: Array<any>
17│ ) => void
18│ ): this
19│ }
20│
我不想在 CustomEventObj
上有一个索引器 属性,因为这不会扼杀只有 3 个可能事件的意义吗?
如有任何帮助,我们将不胜感激。
嗯,我有好消息也有坏消息。好消息是,您将能够让 Flow 了解作为参数传递给 on
处理程序的内容。坏消息是,您将无法告诉 Flow 不允许发生您想要的事件之外的其他事件。
因为您正在扩展 EventEmitter class,所以您明确声明您的 class 将执行与 EventEmitter 相同的操作。因此,由于 EventEmitter 的 on
处理程序将接受任何 string
,您的 "MyEventEmitter" 也必须接受 on
处理程序中的任何字符串。即使你只是抛出一个错误 if/when 你得到一个意想不到的字符串,Flow 仍然希望你遵守那个约定。
现在有个好消息:您可以让 Flow 知道您希望在某些事件中收到什么样的参数。诀窍是指定 on
处理程序的多个声明,如下所示:
// @flow
import EventEmitter from 'events'
type CustomEventObj = {|
pending: void,
success: number,
error: Error
|}
declare class MyEventEmitter extends EventEmitter {
on(
'pending',
// Alternatively, you can pass in a callback like () => void
// as the type here. Either works for the examples below.
(
e: null,
...args: Array<any>
) => void
): this;
on(
'success',
(
e: number,
...args: Array<any>
) => void
): this;
on(
'error',
(
e: Error,
...args: Array<any>
) => void
): this;
// Just to satisfy inheritance requirements, but this should
// throw at runtime.
on(
string,
(
e: mixed,
...args: Array<any>
) => void
): this;
}
let myInstance = new MyEventEmitter()
myInstance.on('pending', () => {}) // works
myInstance.on('success', (num: number) => {console.log(num * 2)}) // works
myInstance.on('success', (badStr: string) => {console.log(badStr.length)}) // Error
现在流理解如果事件是 "success",那么回调应该接受一个数字(或一些包含数字的联合类型,如 string | number
或 any
) .
如果您希望 Flow 仅 允许您传递特定事件,那么您将无法从 EventEmitter class 继承。 (推理是这样的:如果您将 MyEventEmitter 传递给一些传递其他事件的外部代码怎么办?Flow 无法阻止这种情况的发生,并且外部代码也无法知道它无法传递'pending'、'success' 或 'failure')
以外的事件
我已经在 GitHub for trying/reference/forking. I'd post it on https://flow.org/try/ 上发布了我的代码副本,但我似乎无法理解它对 EventEmitter 或 events$EventEmitter 的继承。
警告:我认为您可以为函数指定预期参数的原因是因为 events$EventEmitter 的 on
函数具有 handler
参数 declared as Function
type. This type is a little weird and mostly unchecked。因此,虽然这种在 on
处理程序上进行多重声明的技术适用于 now(Flow 0.69.0),但它可能会与未来版本的 Flow 或 events$EventsEmitter typedef 一起使用。
举个例子,假设我有一个 class,它只发出三个可能的事件 – 'pending'
或 'success'
或 'failure'
。此外,eventHandler
中接收到的参数类型取决于发出的事件 –
- 如果
'pending'
,eventHandler
没有收到参数 - 如果
'success'
,eventHandler
收到number
- 如果
'failure'
,eventHandler
收到Error
以下是我尝试建模的方式:
// @flow
import EventEmitter from 'events'
type CustomEventObj = {|
pending: void,
success: number,
error: Error
|}
declare class MyEventEmitter extends EventEmitter {
on<K: $Keys<CustomEventObj>>(
eventName: K,
eventHandler: (
e: $ElementType<CustomEventObj, K>,
...args: Array<any>
) => void
): this
}
但是,这会导致如下错误:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ test.js:12:3
Cannot extend EventEmitter [1] with MyEventEmitter because an indexer property is missing in CustomEventObj [2] in the
first argument of property on.
[1] 3│ import EventEmitter from 'events'
:
8│ error: Error
9│ |}
10│
11│ declare class MyEventEmitter extends EventEmitter {
[2] 12│ on<K: $Keys<CustomEventObj>>(
13│ eventName: K,
14│ eventHandler: (
15│ e: $ElementType<CustomEventObj, K>,
16│ ...args: Array<any>
17│ ) => void
18│ ): this
19│ }
20│
我不想在 CustomEventObj
上有一个索引器 属性,因为这不会扼杀只有 3 个可能事件的意义吗?
如有任何帮助,我们将不胜感激。
嗯,我有好消息也有坏消息。好消息是,您将能够让 Flow 了解作为参数传递给 on
处理程序的内容。坏消息是,您将无法告诉 Flow 不允许发生您想要的事件之外的其他事件。
因为您正在扩展 EventEmitter class,所以您明确声明您的 class 将执行与 EventEmitter 相同的操作。因此,由于 EventEmitter 的 on
处理程序将接受任何 string
,您的 "MyEventEmitter" 也必须接受 on
处理程序中的任何字符串。即使你只是抛出一个错误 if/when 你得到一个意想不到的字符串,Flow 仍然希望你遵守那个约定。
现在有个好消息:您可以让 Flow 知道您希望在某些事件中收到什么样的参数。诀窍是指定 on
处理程序的多个声明,如下所示:
// @flow
import EventEmitter from 'events'
type CustomEventObj = {|
pending: void,
success: number,
error: Error
|}
declare class MyEventEmitter extends EventEmitter {
on(
'pending',
// Alternatively, you can pass in a callback like () => void
// as the type here. Either works for the examples below.
(
e: null,
...args: Array<any>
) => void
): this;
on(
'success',
(
e: number,
...args: Array<any>
) => void
): this;
on(
'error',
(
e: Error,
...args: Array<any>
) => void
): this;
// Just to satisfy inheritance requirements, but this should
// throw at runtime.
on(
string,
(
e: mixed,
...args: Array<any>
) => void
): this;
}
let myInstance = new MyEventEmitter()
myInstance.on('pending', () => {}) // works
myInstance.on('success', (num: number) => {console.log(num * 2)}) // works
myInstance.on('success', (badStr: string) => {console.log(badStr.length)}) // Error
现在流理解如果事件是 "success",那么回调应该接受一个数字(或一些包含数字的联合类型,如 string | number
或 any
) .
如果您希望 Flow 仅 允许您传递特定事件,那么您将无法从 EventEmitter class 继承。 (推理是这样的:如果您将 MyEventEmitter 传递给一些传递其他事件的外部代码怎么办?Flow 无法阻止这种情况的发生,并且外部代码也无法知道它无法传递'pending'、'success' 或 'failure')
以外的事件我已经在 GitHub for trying/reference/forking. I'd post it on https://flow.org/try/ 上发布了我的代码副本,但我似乎无法理解它对 EventEmitter 或 events$EventEmitter 的继承。
警告:我认为您可以为函数指定预期参数的原因是因为 events$EventEmitter 的 on
函数具有 handler
参数 declared as Function
type. This type is a little weird and mostly unchecked。因此,虽然这种在 on
处理程序上进行多重声明的技术适用于 now(Flow 0.69.0),但它可能会与未来版本的 Flow 或 events$EventsEmitter typedef 一起使用。