【问题标题】:How to declare a Stream-like interface in TypeScript?如何在 TypeScript 中声明类似 Stream 的接口?
【发布时间】:2017-02-01 12:50:24
【问题描述】:

npm 包 csv-parse 实现了类似 Node.JS Stream 的 API:

const parser = parse();
parser.on('readable', () => {
  let record: any;    // <-- it should be string[]
  while (record = parser.read()) {
    output.push(record);
  }
});

它的type declaration 是:

interface parse {
  (options?: options): NodeJS.ReadWriteStream;
} 

parse的返回类型为NodeJS.ReadWriteStream,其中有一个方法read返回string | Buffer

interface ReadWriteStream extends ReadableStream, WritableStream {
  // ...
}

interface ReadableStream extends EventEmitter {
  read(size?: number): string | Buffer;
  // ...
}

根据这个定义,我无法将变量 let record: any = parser.read() 定义为其实际类型 string[]

我曾尝试像这样修改 csv-parse 的类型定义:

interface ParserStream extends NodeJS.ReadWriteStream {
    read(size?: number): string[];    
}

interface parse {
    (options?: options): ParserStream;
}

它不起作用,因为 TypeScript 不允许扩展接口和更改方法的返回类型。

我能想到的是将 NodeJS Stream 更改为通用类型,例如:

interface GenericReadableStream <T> extends EventEmitter {
  read(size?: number): T
  // ...
}

interface ReadableStream extends GenericReadableStream <string | Buffer> {}

那么我可以定义:

interface ParserStream extends GenericReadableStream <string[]> {}

我不知道这是否是一个好方法。它对当前的 Node.JS 类型定义影响很大。

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    首先,CsvParser 实现使用object mode 中的流,因此string | Buffer 对于CsvParser.read 来说并不是一个好的返回类型。

    幸运的是,TypeScript 允许扩展接口并将方法的返回类型更改为any。所以你可以把csv-parse类型定义改成

      interface CsvParser extends NodeJS.ReadWriteStream {
          read(size?: number): any;
    

    允许此代码编译

    let record: string[];  
    while (record = parser.read()) {
    

    但这还不够严格,因为这样也会编译:

    let record: number[];  
    while (record = parser.read()) {
    

    要禁止这种情况,您需要以某种方式声明一个将read 返回类型更改为string[] 的接口。您不能在直接扩展ReadWriteStream 的接口中执行此操作,因为string[]string | Buffer 不兼容。但是,如果您添加中间步骤是可能的 - 一个扩展 ReadWriteStream 并具有与两者兼容的 read 的返回类型的接口。它可以是any 的交集类型和我们可以使用泛型参数T 的所需类型:

    interface ObjectStream<T> extends NodeJS.ReadWriteStream {
        read(size?: number): any & T;
    }
    

    然后你可以让 CsvParser 扩展它:

    interface CsvParser extends ObjectStream<string[]> {
        read(size?: number): string[];
    }
    

    这样的话,你仍然需要修改csv-parser的类型定义,但是不需要修改节点流的类型定义。

    注意:第一次尝试的答案是

      interface CsvParser extends NodeJS.ReadWriteStream {
          read(size?: number): any & string[];
    

    正如@aleung 所注意到的,与

      interface CsvParser extends NodeJS.ReadWriteStream {
          read(size?: number): any;
    

    因为它仍然允许将读取的结果分配给任何类型的变量。

    【讨论】:

    • 我在 TypeScript 2.0.3 上进行了测试。 anystring[] 的交集是 any。所以仍然可以分配给number[]
    • 哦,是的,我把答案简化了,没有检查。您需要在 ReadWriteStream 和 CsvParser 之间有一个中间接口,您可以在其中覆盖 read 以返回 any &amp; string[],然后在 CsvParser 中您可以覆盖它以仅返回 string[]。我会再次检查并更新答案。
    猜你喜欢
    • 2013-09-19
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2017-05-28
    • 2021-01-20
    • 2012-12-30
    相关资源
    最近更新 更多