The npm package csv-parse implements a Node.JS Stream like API:
const parser = parse();
parser.on('readable', () => {
let record: any; // <-- it should be string[]
while (record = parser.read()) {
output.push(record);
}
});
Its type declaration is:
interface parse {
(options?: options): NodeJS.ReadWriteStream;
}
The return type of parse
is NodeJS.ReadWriteStream
, which has a method read
returning string | Buffer
.
interface ReadWriteStream extends ReadableStream, WritableStream {
// ...
}
interface ReadableStream extends EventEmitter {
read(size?: number): string | Buffer;
// ...
}
By this definition, I can't define the variable let record: any = parser.read()
to its actual type string[]
.
I have tried to modify the type definition of csv-parse like this:
interface ParserStream extends NodeJS.ReadWriteStream {
read(size?: number): string[];
}
interface parse {
(options?: options): ParserStream;
}
It doesn't work because TypeScript not allow to extend an interface and change method's return type.
What I can think of is to change NodeJS Stream to a generic type like:
interface GenericReadableStream <T> extends EventEmitter {
read(size?: number): T
// ...
}
interface ReadableStream extends GenericReadableStream <string | Buffer> {}
Then I can define:
interface ParserStream extends GenericReadableStream <string[]> {}
I don't know if it's a good way. It impacts a lot in current Node.JS type definitions.
To begin with, CsvParser implementation uses steams in object mode, so string | Buffer
isn't really a good return type for CsvParser.read
.
Fortunately, TypeScript allows to extend an interface and change method's return type to any
. So you can change csv-parse
type definition to
interface CsvParser extends NodeJS.ReadWriteStream {
read(size?: number): any;
allowing this code to compile
let record: string[];
while (record = parser.read()) {
But this is not strict enough, because this will compile too:
let record: number[];
while (record = parser.read()) {
To disallow that, you somehow need to declare an interface that changes read
return type to string[]
. You can't do that in an interface that directly extends ReadWriteStream
because string[]
is incompatible with string | Buffer
. However it's possible if you add intermediate step - an interface that extends ReadWriteStream
and has return type for read
compatible with both. It can be an intersection type of any
and the desired type for which we can use generic parameter T
:
interface ObjectStream<T> extends NodeJS.ReadWriteStream {
read(size?: number): any & T;
}
Then you can have CsvParser extending it:
interface CsvParser extends ObjectStream<string[]> {
read(size?: number): string[];
}
That way, you still need to modify csv-parser
type definitions, but there is no need to change type definitions for node streams.
NOTE: the first attempt at the answer was
interface CsvParser extends NodeJS.ReadWriteStream {
read(size?: number): any & string[];
which, as @aleung noticed, is no different from
interface CsvParser extends NodeJS.ReadWriteStream {
read(size?: number): any;
because it still allows to assign the result of read to a variable of any type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With