Typescript 2 – Interface & Classes

1.tsconfig.json

{
    "compilerOptions": {
		"target": "es6",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
		"module": "es2015",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
		"outDir": "./public",                        /* Redirect output structure to the directory. */
		"rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
		"strict": true,                           /* Enable all strict type-checking options. */
		"esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
		"forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
	},
    "include": ["src"]
}

2.src/app.js

import { Invoice } from './classes/Invoice.js';
import { Payment } from './classes/Payment.js';
import { ListTemplate } from './classes/ListTemplate.js';
import { HasFormatter } from './interfaces/HasFormatter.js';

// let docOne: HasFormatter;
// let docTwo: HasFormatter;

// docOne = new Invoice('yoshi', 'web work', 250);
// docTwo = new Payment('mario', 'plumbing', 200);

// let docs: HasFormatter[] = [];
// docs.push(docOne);
// docs.push(docTwo);

const form = document.querySelector('.new-item-form') as HTMLFormElement;
console.log(form.children);

// inputs
const type = document.querySelector('#type') as HTMLInputElement;
const tofrom = document.querySelector('#tofrom') as HTMLInputElement;
const details = document.querySelector('#details') as HTMLInputElement;
const amount = document.querySelector('#amount') as HTMLInputElement;

// list template instance
const ul = document.querySelector('ul')!;
const list = new ListTemplate(ul);

form.addEventListener('submit', (e: Event) => {
  e.preventDefault();

  let doc: HasFormatter;
  if (type.value === 'invoice') {
    doc = new Invoice(tofrom.value, details.value, amount.valueAsNumber);
  } else {
    doc = new Payment(tofrom.value, details.value, amount.valueAsNumber);
  }
  list.render(doc, type.value, 'end');
});

3. src/interfaces/HasFormatter.ts

export interface HasFormatter {
    format(): string;
  }

4. src/classes/Invoice.ts

import { HasFormatter } from '../interfaces/HasFormatter.js';

export class Invoice implements HasFormatter {
  constructor(
    readonly client: string, 
    private details: string, 
    public amount: number,
  ){}

  format() {
    return `${this.client} owes £${this.amount} for ${this.details}`;
  }
}

5. src/classes/Payment.ts

import { HasFormatter } from '../interfaces/HasFormatter.js';

export class Payment implements HasFormatter{
  constructor(
    readonly recipient: string,
    private details: string,
    public amount: number,
  ){};

  format() {
    return`${this.recipient} is owed £${this.amount} for ${this.details}`;
  }
}

5. public/index.html

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TypeScript Tutorial</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>

  <div class="wrapper">
    <h1>Finance Logger</h1>

    <!-- output list -->
    <ul class="item-list">
      
    </ul>
  </div>

  <footer>
    <form class="new-item-form">
      <div class="field">
        <label>Type:</label>
        <select id="type">
          <option value="invoice">Invoice</option>
          <option value="payment">Payment</option>
        </select>
      </div>
      <div class="field">
        <label>To / From:</label>
        <input type="text" id="tofrom">
      </div>   
      <div class="field">
        <label>Details:</label>
        <input type="text" id="details">
      </div>
      <div class="field">
        <label>Amount (£):</label>
        <input type="number" id="amount">
      </div>
      <button>Add</button>
    </form>

    <a href="https://www.thenetninja.co.uk">The Net Ninja</a>
  </footer>

  <script type="module" src='app.js'></script>
</body>
</html>

6.src/classes/ListTemplate.ts

import { HasFormatter } from "../interfaces/HasFormatter";

export class ListTemplate {
  constructor(private container: HTMLUListElement){}

  render(item: HasFormatter, heading: string, pos: 'start' | 'end'){
    const li = document.createElement('li');
  
    const h4 = document.createElement('h4');
    h4.innerText = heading;
    li.append(h4);

    const p = document.createElement('p');
    p.innerText = item.format();
    li.append(p);

    if(pos === 'start'){
      this.container.prepend(li);
    } else {
      this.container.append(li);
    }
  }
}

Reference

Ninja
Git

Leave a Reply

Your email address will not be published. Required fields are marked *