Monthly Archives: May 2019

Method overloading in Typescript

Method overloading within TypeScript feels slightly strange, especially if you’re coming from a language such as C#.

In essence we declare the overloads but only have a single implementation which should handle the alternate arguments from the declared overloads by checking the types passed in. Don’t worry, we’ll look at an example which will make this clear.

The first thing to know about TypeScript overloads is if we declare overloads with the same number of arguments in the methods, these will be transpiled to JavaScript and the static types of the arguments is lost.

In other words, if you have

class Options {
   static if<T>(value: T, condition: boolean): void {
   }
   static if<T>(value: T, numberParameter: number): void {
   }
}

the type information would be lost when transpiled, effectively meaning both methods would look like this

static if(value, condition);

So how do we do method overloading in TypeScript?

We can create method overloads with different numbers of arguments within TypeScript, but we “declare” the method signatures without implementations, for example

class Options {
   static if<T>(value: T, condition: boolean): void;
   static if<T>(value: T, numberParameter: number, stringParameter: string): void;

   // implementation to follow
}

As can be seen we have two overloads of the if method taking two and three arguments respectively, but these methods have no implementation. Instead we need to create another overload which can take all the arguments.

In this case we have a method with two arguments and one with three, hence our implementation needs to take three arguments. We then need to either match the argument types (if all overloads take the same type for any argument) or handle different types via a union or an any type and then check the type passed into the method using typeof or instanceof.

So for example, here’s an implementation of a method which can handle the types passed to either of the “declared” overrides

static if<T>(value: T, stringOrNumberParameter: any, stringParameter?: string): void {
   if (stringOrNumberParameter && typeof stringOrNumberParameter == "number") {
      // handle the second argument as a number
   }
   else {
      // handle the second argument as a string
   }
}

Notice how the third argument needs to be optional or have a default so that we can still use the two argument overload.

Hence, we end up with the following

class Options {
   static if<T>(value: T, condition: boolean): void;
   static if<T>(value: T, numberParameter: number, stringParameter: string): void;
    
   static if<T>(value: T, stringOrNumberParameter: any, stringParameter?: string): void {
      if (stringOrNumberParameter && typeof stringOrNumberParameter == "number") {
         // handle the second argument as a number
      }
      else {
         // handle the second argument as a string
      }
   }
}

We can implement overloads with the same number of arguments, within TypeScript. Again the types are lost once transpiled, but let’s take this example

function run(option: null): null;
function run(option: number): number;
function run(option: string): string;
function run(option: any): any {
  if(typeof option == "string") {
    // string implementation
  }
}

which again gets transpiled without types to

function run(option) {
}

however during development the types are respected. In such scenarios one might also use unions, such as

function run(option: string | number | null) {
}

Zero to web with TypeScript, webpack and more

This post is a little long but the aim is to cover creating a simple TypeScript/HTML website without using any frameworks but using a whole bunch of standard tools.

The website will be pathetically unimpressive because I want to solely concentrate on just getting the various technologies working together.

We’ll be using Visual Code as the editor. We’ll be using TypeScript, ESLint and Jest (as per previous posts on this topics) and we’ll be using webpack to both create a distribution of our code and to host our code.

Creating the basics

  • Create the project’s folder, mine’s zerotoweb
  • Open Visual code against the new folder
  • Use the key combination (on mine it’s CTRL + ‘ on Windows) to open the terminal within Visual Code or if you prefer open a command prompt and navigate to the the folder you created
  • run yarn init –yes within the terminal. This will create the package.json file
  • run tsc –init within the terminal to create the tsconfig.json file
  • Change tsconfig.json to the following contents
    {
      "compilerOptions": {
        "target": "es6",
        "outDir": "./public",
        "rootDir": "./src",
        "allowJs": true,
        "skipLibCheck": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "noImplicitAny": false
      },
      "include": [
        "src"
      ],
      "exclude": [
        "node_modules"
      ]
    }
    
  • Next create the src folder off of the root
  • Add a file named index.ts to src folder and add the following code
    class App {
        getSalutation() {
            return "Hello World"
        } 
    }
    
    let app = new App();
    console.log(app.getSalutation());
    
  • We’ll want to run from node, which currently doesn’t support es6 so we’ll need to add babel, so run
    • yarn add babel-cli -D
    • yarn add babel-preset-es2015 -D
    • yarn add babel-preset-env -D
  • Let’s now add a couple of scripts to package.json
    "scripts": 
    {
       "build": "tsc --watch", 
       "start": "babel-node --presets es2015 ./public/index.js"
    }
    
  • Now we need to add a configuration file for babel, so add a .babelrc file to the root folder and put the following within it
    {
       "presets": ["env"]
    }
    
  • Next we want to add ESLint (this section is a duplication of the previous post but is here for completeness), so run the following
    • yarn add eslint -D
    • yarn add @typescript-eslint/parser -D
    • yarn add @typescript-eslint/eslint-plugin -D
    • Add a .eslintrc.js file to the root folder, place the following into the file
      module.exports = {
          "parser": '@typescript-eslint/parser',
          "plugins": ['@typescript-eslint'],
          "extends": ['plugin:@typescript-eslint/recommended'],
          "rules": {
              "@typescript-eslint/no-parameter-properties": "off",
              "@typescript-eslint/no-explicit-any": "off"
          }
      };
      
    • Now add the following “lint”: “eslint ./src/*.ts” to the scripts section of the package.json
  • Now run yarn build
  • Lets see some output, so run yarn start. If all went well you should see Hello World output.

Adding tests

I’ve covered installing Jest for testing React applications previously but let’s see the steps to take to get everything installed for our non-React world

  • yarn add jest -D
  • yarn add @types/jest -D
  • yarn add ts-jest -D
  • yarn add @types/node -D (not sure about this one)
  • Add __tests__ under the src folder
  • Add TypeScript tests to the __tests__ folder, convention suggests {name}.test.ts for the filenames
  • Add “test”: “jest” to the scripts section of packages.json if you want to run the tests yourself (i.e. no plugin or watch)
  • Finally in the root folder add the file jest.config.js with the following code
    module.exports = {
        roots: ['./src'],
        transform: {
          '^.+\\.tsx?$': 'ts-jest',
        },
        testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
        moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
      }
    

In Visual Code the plugin for Jest (Use Facebok’s Jest With Pleasure) from Orta is very useful to have.

Test coverage

Jest includes an option for running code coverage, simply change your packages.json script to

  • “test”: “jest –coverage”

Adding webpack

Next up we’re going to create our basic web site, extending on what we’ve already covered.

  • Create folder name public off of the root folder
  • Add a file named index.html to public folder, here’s the HTML
    <html>    
        <body>
            <script src="./index.js"></script>
        </body>
    </html>
    
  • Run the following commands
    • yarn add webpack -D
    • yarn add webpack-cli -D
    • yarn add webpack-dev-server -D
    • yarn add babel-loader@7 -D (@7 was required for bable-core)
    • yarn add babel-core -D
  • Create a webpack.config.js in the root folder here’s mine
    var path = require("path");
    module.exports = {
       entry: {
         app: ["./public/index.js"]
       },
      output: {
        path: path.resolve(__dirname, "build"),
        filename: "bundle.js"
      },
      devServer: {
        port: 9000,
        contentBase: "./public"
      },
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["babel-preset-env"]
              }
            }
          }
        ]
      }
    };
    
  • Now replace the console.log line within the index.ts file with the following
    document.body.innerHTML = app.getSalutation();
    
  • Replace the previously created “start” script with “start”: “webpack-dev-server –open” in packages.json “scripts” section
  • Let’s run yarn start and if all went well you should see a browser window open and display our HTML page with the JavaScript created from our TypeScript file displayed

Time to create bundles

We’re going to use webpack to create some distribution bundles

  • Run the following commands
    • yarn add express -D
    • yarn add webpack-dev-middleware -D
    • yarn add html-webpack-plugin -D
    • yarn add clean-webpack-plugin -D
  • Change the webpack.config.json to
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    module.exports = {
      mode: 'development',
      entry: {
        app: './public/index.js'
      },
      devtool: 'inline-source-map',
      devServer: {
        port: 9000,
        contentBase: './dist'
      },
      plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          title: 'Demo'
        })
      ],
      output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/'
      }
    };
    
  • Add “bundle”: “webpack” to the scripts section of the packages.json file. This will create a dist folder with, both the HTML file and the JS files we’ve created and generated

Minifying

You’ll notice that the JavaScript file generated in the previous steps is not exactly small, containing comments, whitespace etc. So we’re going to minify it for distribution.

  • Run yarn add babel-minify-webpack-plugin -D
  • Within the webpack.config.js file, comment out devtool: ‘inline-source-map’,
  • Add this line to the top of the file
    const MinifyPlugin = require("babel-minify-webpack-plugin");
    
  • Add new MinifyPlugin to the file, so it looks like this
    plugins: [
       new CleanWebpackPlugin(),
       new HtmlWebpackPlugin({
          title: 'Demo'
       }),
       new MinifyPlugin()
    ],
    
  • Finally, run our script yarn bundle

Trying an alternate minify

Let’s try terser

  • Run yarn add terser-webpack-plugin -D
  • Add this line to the top of webpack.config.js
    const TerserPlugin = require('terser-webpack-plugin');
    
  • Add the following to webpack.config.js
    optimization: {
        minimizer: [new TerserPlugin({
          extractComments: true,
          test: /\.js(\?.*)?$/i,
        })],
      },
    

The optimization will need to be run webpack in production mode, i.e. webpack –mode=production or webpack-dev-server –open –mode=production or in webpack.config.js set mode: ‘production’.

Hence using yarn we can run yarn bundle –mode=production to see the bundle.js has been minified using terse. Obviously you can remove the new MinifyPlugin() also at this point if using TerserPlugin.

Using ESLint on TypeScript files

Assuming you already have a project set-up. If you don’t have typescript within your node modules, add it using

  • yarn add typescript

Next up we’ll add eslint and it’s typescript plugins (to the dev dependencies)

  • yarn add –dev eslint
  • yarn add –dev @typescript-eslint/parser
  • yarn add –dev @typescript-eslint/eslint-plugin

ESList uses a configuration file in the shape of a .eslintrc.js file which should be placed in root folder of your project. Below is a sample

module.exports = {
    "parser": '@typescript-eslint/parser',
    "plugins": ['@typescript-eslint'],
    "extends": ['plugin:@typescript-eslint/recommended'],
    "rules": {
        "@typescript-eslint/no-parameter-properties": "off",
        "@typescript-eslint/no-explicit-any": "off"
    },
};

In the above we’ve turned off a couple of rules (the rule name will be listed alongside output from eslint when it’s run).

Now, to run eslint we use the following command

.\node_modules\.bin\eslint ./src/*.ts

References

ESLint Rules
ESLint Plugin

Creating a sample JavaScript package

In my previous post I covered some steps to use yalc to create local packages. Let’s look at creating a very simple package.

We’ll create a simple little mathematics package which will include functions to add, subtract, divide and multiply.

  • Create a folder for our package, for example Math
  • Add an index.js file which should look like this
    'use strict';
    
    module.exports = {
        add,
        subtract,
        divide,
        multiply
    }
    
    function add(a, b) {
        return a + b;
    }
    
    function subtract(a, b) {
        return a - b;
    }
    
    function divide(a, b) {
        return a / b;
    }
    
    function multiply(a, b) {
        return a * b;
    }
    
  • Now, as we’re going to want to turn this into a package, run
    yarn init --yes
    
  • Edit the package.json file to look like this
    {
      "name": "Math",
      "version": "1.0.0",
      "license": "MIT",
      "private": true
    }
    

Let’s look at what we’ve done, the use of index.js as opposed to say Mathmatics.js is this will then make referencing the file much simpler within the code importing the file, for example we can import like this

import Math from 'Math';

var result = Math.add(2, 5);

We’ve used strict mode to restrict the some JavaScript features but also enhances prevention of silly mistakes.

Next we need to export (at the module level) our functions. In the example we export multiple functions. We could also use the following syntax

module.exports = {
   add: function(a, b) 
   {
      return a + b;
   },
   subtract: function(a, b) 
   {
      return a - b;
   }
   // etc.
}

Another alternative syntax is

exports.add = function(a, b) {
   return a + b;
}

exports.subtract = function(a, b) {
   return a - b;
}
// etc.

Now you can package this up using yalc (as per my previous post on yalc).

Yalc

Yalc is a package authoring tool, allowing us to create packages from JavaScript libraries locally. This is useful during package authoring and for testing as well as just plain, allowing us to create our own local packages and not need to have them distributed to some remote registry.

Installing yalc

To install yalc, execute the following command

yarn global add yalc

// or using npm

npm i yalc -g

Creating a package locally

First off we need a package.json file, so the simplest way is to run

yan init --yes

this will create a default package.json file which you should edit, so for example removing main and adding the private key, which will ensure the package cannot accidentally get published. For example

{
  "name": "MyPackage",
  "version": "1.0.0",
  "license": "MIT",
  "private": true
}

Running the following command will create a package based upon the package.json and files within the same folder as the package.json. As we’ve marked the package.json as private we’ll need to use the –private switch to force the package creation

yalc publish --private

The package will be stored in the folder listed below on Windows, obviously replacing the {username} with your user name and {packagename} with your package name.

C:\Users\{username}\AppData\Local\Yalc\packages\{packagename}

Using the newly created package

We’ll obviously now want to use our local package, so within your application run, again replacing the {packagename} with your newly created package name.

yalc add {packagename}

This command will add the package to your package.json in your application with a file: prefix. Obviously your package will also be added to your node_modules folder.

I’m not quite sure why, but I also found that this was a very useful technique for some code which seemed to have module problems when I tried to add the code to my application. Once packaged, the module issues went away.

Gulp

Whilst package manager’s such as yarn and npm make things pretty easy, sometimes we’ll want to extend our build workflow or the likes. Yarn allows the use of scripts, but another alternative would be a build tool such as Gulp. Ofcourse we could also write scripts in bash or Powershell etc. but Gulp offers a DSL in a similar way to tools such as Cake, Nant etc. to extend our various development and build workflows.

Getting started

A Gulp file is simply a JavaScript file (.js) in which we have the lines

var gulp = require('gulp');
// if we want to include yarn support in gulp
var yarn = require('gulp-yarn');

these include the gulp functions.

To create a gulp task we include the following

gulp.task('yarn-production', function() {
   // return from the function()
});

The string yarn-production is the task name, i.e. we can run the following command to execute our task

gulp yarn-production

Api’s

gulp.task

As we’ve seen gulp.task is one of the gulp API’s which defines a task along with the task name, which can be executed via the gulp command line, followed by the function to run for that task.

gulp.src

The src API allows us to declare an array of filenames/globs to be passed into the gulp pipeline, for example

gulp.task('yarn-production', function() {
    return gulp.src(['./package.json', './yarn.lock'])
       // pipeline
    );
});

This command basically creates an array of files etc. that will be used in the subsequent pipeline.

gulp.dest

We’ve got a src, so obviously we need a dest which denotes (as you’d expect) where the output from the pipeline goes to. So here’s an example which copies some files to a production folder

gulp.task('production', function() {
    return gulp.src(['./package.json', './yarn.lock'])
        .pipe(gulp.dest('production/'));
});

The .pipe creates the workflow of the gulp task.

The .pipe can also take a function, for example

gulp.task('production', function() {
    return gulp.src(['./package.json', './yarn.lock'])
        .pipe(log)
        .pipe(gulp.dest('production/'));
});

I’m not going to cover all the gulp functions as you can visit Gulp/Concepts for a list of the current API.

Writing our own pipe function

We’ve seen some example of using the pipe function with gulp functions but what about, if we want to write out own pipe functions.

Here’s an example which simply logs the files that are to be transformed

var gulp = require('gulp');
var through = require('through2');

function log() {
    return through.obj((file, encoding, callback) =>
    {
        console.log(file.path);
        return callback(null, file);
    });
}

gulp.task('production', function() {
    return gulp.src(['./package.json', './yarn.lock'])
        .pipe(log());
});

In this sample we simply log the file path, but if (for example) we wanted to make some changes to the files (transform them) we’d use something like this

var gulp = require('gulp');
var through = require('through2');

function configure() {
    return through.obj((file, encoding, callback) =>
    {   
        var transformedFile = file.clone();
        transformedFile.contents = new Buffer("Transformed file contents");
        callback(null, transformedFile);
    });
}

gulp.task('production', function() {
    return gulp.src(['./package.json'])
        .pipe(configure())
        .pipe(gulp.dest('production/'));
});

This will simply output files with the src names with the context “Transformed file” within it.

Generators in JavaScript and TypeScript

Generators within JavaScript (and TypeScript) are similar to C# IEnumerable’s such that you can yield a value multiple times from a generator function (in either JavaScript or TypeScript), i.e.

function* someValues() {
    yield 1;
    yield 2;
    yield 3;
}

or within a TypeScript class we can write

export default class MyClass {
    *someValues() {
       yield 1;
       yield 2;
       yield 3;
    }
}

To use the someValues function we can loop using

for(let i of someValues()) {
    console.log(i);
}

Each item returned is a type of { value, done }, so for example

var values = someValues();

console.log(values.next());
console.log(values.next());
console.log(values.next());
console.log(values.next());

which would output

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }

Using lodash in TypeScript

Was trying to use lodash and couldn’t seem to get Visual Code to build my TypeScript files correctly, so here’s how to get it working…

Add the following esModuleInterop to your tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "esModuleInterop": true,
        "sourceMap": true
    }
}

and then you can use

import _ from "lodash";

Otherwise you can use the following if esModuleInterop doesn’t exist or is set to false

import * as _ from "lodash";

Unit testing and React

If you’ve created a React application using yarn, you’ll actually have the script for yarn test as well as App.test.tsx for running unit tests against React code (i.e. capable of running against tsx/jsx files etc.).

Jest is used for unit testing by default, but if you need to install Jest yourself simple run

yarn add --dev jest

The test script (within package.json) will execute react-scripts test but if we want to add our own script you could add

"jest": "jest --watch"

–watch will allow jest to monitor file changes and rerun tests automatically.

Create a folder off of your src folder (it can be at any depth) named __tests__ and within this we’ll add a simple .js file (or .ts) named files, for example number.test.js which we’ll create as a simple demonstration of writing tests and running the test runner. We should use either .test. or .spec. within the filename.

Within my number.test.js file I have the following

export function getNumber() {
    return 1234;
}

test('Test 1', () => {
    expect(getNumber()).toEqual(123);
});

test('Test 2', () => {
    expect(getNumber()).toEqual(1234);
});

Note: we can use “it” instead of “test” in the above

Obviously we’d normally not have the actually function we want to test within the test file, this is solely for simplicity of writing this post.

As you can see, each test comes with a string which is the name/description of the test, followed by a function which is executed by the test runner. Obviously in the above code “Test 1” will fail and “Test 2” will pass, so let’s run the test runner and see.

Like other test frameworks, we have functions to assert/expect certain values within our tests.

If you’ve added the script now run yarn jest or use yarn test. The Jest test runner will run and remain running, watching for file/test changes. Select the “a” option to run all tests after executing the jest script.

From the test runner (jest) you might wish to filter file name by regex, simply running tests against the __test__ folder.

React Router

There are many way to route web calls to our web pages etc. React also comes with it’s own capability, which we can load by running

yarn add react-router-dom
yarn add @types/react-router-dom

If you’ve created your React application using yarn create react-app –typescript then go to the index.tsx and edit it to look like this

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter, Route } from 'react-router-dom'
import * as serviceWorker from './serviceWorker';
import About from './components/About';
import Home from './components/Home';

ReactDOM.render((    
    <BrowserRouter>
      <Route exact path="/home" component={Home} />
      <Route exact path="/about" component={About} />
  </BrowserRouter>), 
    document.getElementById('root'));

serviceWorker.unregister();

Now, let’s create a couple of React components which will act as our routes. Create a folder named components off of the root folder and add two files, about.tsx and home.tsx to the folder. The code for each is shown below (and the files should go in the folder src/components)

// about.tsx

import React, { Component } from 'react';

export default class About extends Component {
    render() {
        return (
            <div>About</div>
        );
    }
}

and

// home.tsx

import React, { Component } from 'react';

export default class Home extends Component {
    render() {
        return (
            <div>Home</div>
        );

    }
}

We’re not using the App.* files, so they can be deleted.

Obviously we can also add a root route, i.e.

<Route exact path="/" component={Home} />