Allow InputLine to specify a fallback value.
[ustaxlib.git] / src / Line.ts
1 import TaxReturn from './TaxReturn';
2 import Form, { FormClass } from './Form';
3
4 export abstract class Line<T> {
5 private _description?: string;
6
7 _id: string; // _id is set by Form.init().
8 form: Form<any, any>; // Set by Form.init();
9
10 constructor(description?: string) {
11 this._description = description;
12 }
13
14 get id(): string {
15 return this._id;
16 }
17
18 get description(): string {
19 return this._description;
20 }
21
22 abstract value(tr: TaxReturn): T;
23 };
24
25 type ComputeFunc<T> = (tr: TaxReturn) => T;
26
27 export class ComputedLine<T> extends Line<T> {
28 private _compute: ComputeFunc<T>;
29
30 constructor(compute: ComputeFunc<T>, description?: string) {
31 super(description);
32 this._compute = compute;
33 }
34
35 value(tr: TaxReturn): T {
36 return this._compute(tr);
37 }
38 };
39
40 export class ReferenceLine<F extends Form<any>,
41 L extends keyof F['lines'],
42 T extends ReturnType<F['lines'][L]['value']>>
43 extends Line<T> {
44 private _form: FormClass<F>;
45 private _line: L;
46 private _fallback?: T;
47
48 // If creating a ReferenceLine and F is the same class as the
49 // the one the Line is in, erase |form|'s type with |as any| to
50 // keep TypeScript happy.
51 constructor(form: FormClass<F>, line: L, description?: string, fallback?: T) {
52 super(description || `Reference F${form}.L${line}`);
53 this._form = form;
54 this._line = line;
55 this._fallback = fallback;
56 }
57
58 value(tr: TaxReturn): T {
59 const form: F = tr.findForm(this._form);
60 if (this._fallback !== undefined && !form)
61 return this._fallback;
62 const value: T = form.getValue(tr, this._line);
63 return value;
64 }
65 };
66
67 export class InputLine<U = unknown, T extends keyof U = any> extends Line<U[T]> {
68 private _input: T;
69 private _fallback: U[T];
70
71 form: Form<any, U>;
72
73 constructor(input: T, description?: string, fallback?: U[T]) {
74 super(description || `Input from ${input}`);
75 this._input = input;
76 this._fallback = fallback;
77 }
78
79 value(tr: TaxReturn): U[T] {
80 if (!this.form.hasInput(this._input) && this._fallback !== undefined)
81 return this._fallback;
82 return this.form.getInput<T>(this._input);
83 }
84 };
85
86 export class AccumulatorLine<F extends Form<any>,
87 L extends keyof F['lines']>
88 extends Line<number> {
89 private _form: FormClass<F>;
90 private _line: L;
91
92 constructor(form: FormClass<F>, line: L, description?: string) {
93 super(description || `Accumulator F${form}.L${line}`);
94 this._form = form;
95 this._line = line;
96 }
97
98 value(tr): number {
99 const forms: F[] = tr.findForms(this._form);
100 return sumLineOfForms(tr, forms, this._line);
101 }
102 };
103
104 export function sumLineOfForms<F extends Form<any>, L extends keyof F['lines']>(
105 tr: TaxReturn, forms: F[], line: L): number {
106 const reducer = (acc: number, curr: F) => acc + curr.getValue(tr, line);
107 return forms.reduce(reducer, 0);
108 }