diff --git a/.eslintrc.js b/.eslintrc.js index 250ebb7..c264b79 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { + root: true, parser: '@typescript-eslint/parser', // Specifies the ESLint parser plugins: ["@typescript-eslint"], extends: [ diff --git a/README.md b/README.md index 0d2a124..60e0fcc 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ This lib provide decorator for fixed width file so that the domain class and spe ## Example This lib uses [reflect-metadata](https://github.com/rbuckton/reflect-metadata) for Meta programming. +Inheritance is supported that fields decorated in parent classes will also be included/processed. + ### Model definition ```typescript @@ -17,7 +19,7 @@ export class Transaction extends FixedWidthConvertible { @FixedWidth({ start: 5, width: 40 }) parentName: string; - + @FixedWidth({ start: 45, width: 40, format: { type: DataType.Float } }) taxAmount: number; diff --git a/package.json b/package.json index b4b74f5..e46b39a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fixed-width-ts-decorator", - "version": "1.1.0", + "version": "1.2.0", "description": "Fixed width file handler with TypeScript Decorator", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/fixed-width-convertible.ts b/src/fixed-width-convertible.ts index 140aff1..9653a52 100644 --- a/src/fixed-width-convertible.ts +++ b/src/fixed-width-convertible.ts @@ -6,7 +6,7 @@ export abstract class FixedWidthConvertible { } static convertFixedWidth>(line: string, target: T): T { - const fields = Reflect.getMetadata(fixedWidthVariableKey, target.constructor, fixedWidthVariableKey); + const fields = this.getAllFields(target.constructor); for (const field of fields) { const options: FixedWidthOptions = Reflect.getMetadata(fixedWidthMetadataKey, target, field); const value = line.substring(options.start, options.start + options.width).trim(); @@ -21,5 +21,17 @@ export abstract class FixedWidthConvertible { } return target; } + + /** + * @param clz the class/constructor + * @returns the fields decorated with @FixedWidth all the way up the prototype chain. + */ + static getAllFields(clz: Record): string[] { + if(!clz) return []; + const fields: string[] | undefined = Reflect.getMetadata(fixedWidthVariableKey, clz, fixedWidthVariableKey); + // get `__proto__` and (recursively) all parent classes + const rs = new Set([...(fields || []), ...this.getAllFields(Object.getPrototypeOf(clz))]); + return Array.from(rs); + } } diff --git a/src/fixed-width-decorator.test.ts b/src/fixed-width-decorator.test.ts index efb2833..02839c1 100644 --- a/src/fixed-width-decorator.test.ts +++ b/src/fixed-width-decorator.test.ts @@ -1,5 +1,6 @@ import fs from 'fs'; import { FixedWidthConvertible } from './fixed-width-convertible'; +import { ChildTransaction } from './scripts/ChildTransaction'; import { Transaction } from './scripts/Transaction'; describe('fixed width file test', () => { @@ -35,5 +36,22 @@ describe('fixed width file test', () => { expect(rs[1].taxAmount).toBe(123.45); expect(rs[2].taxAmount).toBe(12.35); expect(rs[0].clientId).toBe('20000'); + // should not have child class field + expect((rs[0] as any).paymentId).toBeUndefined(); }); + + test('should get Field from parent class', () => { + const rs: Array = lines.map(line => { + const trans = new ChildTransaction(); + FixedWidthConvertible.convertFixedWidth(line, trans); + return trans; + }); + expect(rs.length).toBe(4); + // should have parent class fields + expect(rs[1].taxAmount).toBe(123.45); + expect(rs[2].taxAmount).toBe(12.35); + expect(rs[0].clientId).toBe('20000'); + expect(rs[0].paymentId).not.toBeUndefined(); + }); + }); diff --git a/src/scripts/ChildTransaction.ts b/src/scripts/ChildTransaction.ts new file mode 100644 index 0000000..81f0a39 --- /dev/null +++ b/src/scripts/ChildTransaction.ts @@ -0,0 +1,7 @@ +import { FixedWidth } from '../fixed-width-decorator'; +import { Transaction } from './Transaction'; + +export class ChildTransaction extends Transaction { + @FixedWidth({ start: 225, width: 40 }) + paymentId: string; +} diff --git a/src/scripts/Transaction.ts b/src/scripts/Transaction.ts index 8d47508..8b8f905 100644 --- a/src/scripts/Transaction.ts +++ b/src/scripts/Transaction.ts @@ -7,7 +7,7 @@ export class Transaction extends FixedWidthConvertible { @FixedWidth({ start: 5, width: 40 }) parentName: string; - + @FixedWidth({ start: 45, width: 40, format: { type: DataType.Float } }) taxAmount: number; @@ -31,8 +31,6 @@ export class Transaction extends FixedWidthConvertible { @FixedWidth({ start: 145, width: 40 }) userId: string; - @FixedWidth({ start: 225, width: 40 }) - paymentId: string; // other non-decorated fields otherField: string; } diff --git a/src/scripts/trigger.ts b/src/scripts/trigger.ts index ecc7c32..c65df8a 100644 --- a/src/scripts/trigger.ts +++ b/src/scripts/trigger.ts @@ -1,12 +1,13 @@ import 'reflect-metadata'; import fs from 'fs'; import { Transaction } from './Transaction'; +import { ChildTransaction } from './ChildTransaction'; (() => { const file = fs.readFileSync('data/sample.txt', { encoding: 'utf8' }); const lines = file.split('\n').filter(l => l); const rs: Array = lines.map(line => { - const trans = new Transaction(); + const trans = new ChildTransaction(); trans.convertFixedWidth(line); // FixedWidthConvertible.convertFixedWidth(line, trans); return trans;