2016-09-23 3 views
1

НПМ пакет csv-parse реализует Node.js поток как API:Как объявить Stream-подобный интерфейс в TypeScript?

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-синтаксический анализ, как это:

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

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

Это не работает, потому что машинопись не позволяет расширить интерфейс и изменить тип возвращаемого значения.

То, что я могу думать о том, чтобы изменить NodeJS поток для универсального типа, как:

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

interface ReadableStream extends GenericReadableStream <string | Buffer> {} 

Тогда я могу определить:

interface ParserStream extends GenericReadableStream <string[]> {} 

Я не знаю, если это хороший способ. Это сильно влияет на текущие определения типа Node.JS.

ответ

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; 

, потому что она по-прежнему позволяет присвоить результат чтения к переменная любого типа.

+1

Я тестировал на TypeScript 2.0.3. Intersaction 'any' и' string [] 'is' any'. Таким образом, по-прежнему можно назначить 'number []'. – aleung

+0

О да, я упростил ответ и не проверял. Вам нужен один промежуточный интерфейс между ReadWriteStream и CsvParser, в котором вы переопределяете 'read', чтобы возвращать' any & string [] ', а затем в CsvParser вы можете переопределить его, чтобы вернуть только' string [] '. Я снова проверю и обновлю ответ. – artem

+0

Я обновил ответ - я думаю, он должен работать сейчас – artem

Смежные вопросы