Yarn and what not to commit to your source repos

When working with yarn you’ll find your source code folder includes the following files and folder (amongst others)

  • packages.json
  • yarn.lock
  • node_modules

If using TypeScript you may also have tsconfig.json.

The node_modules folder is basically everything downloaded via yarn that are either included within the packages.json or dependencies of those packages. So unless you’re fearful of versions of mdoules/dependencies becoming unavailable, this folder can be left out of source control, plus it can get big quickly.

The packages.json file should be committed as it obviously includes all the information regarding the packages used and their versions, but is also used for scripts etc.

Interestingly, I thought that yarn.lock should also be excluded as it’s generated by yarn, but according to Lockfiles should be committed on all projects.

If you have tsconfig.json, this file is a TypeScript configuration file. Hence this should be committed to ensure other team members are using the same configuration.

What about .vscode

If you’re using VSCode as your editor/development tool then you’ll notice that a .vscode folder is created in some cases with settings.json etc. within it.

Obviously whether you commit .vscode (or individual files within it) depends on whether you’re also aiming to commit shared settings etc. To my mind this would be a good thing as I use multiple machines for development and it’s an easy way for me to share settings between machines. This said if you’re a larger team it also might be useful to ensure everyone uses the same settings.

Using the Windows Subsystem for Linux

Whilst I have Linux machines available to me, they’re not always powered up or the remoting

See Windows Subsystem for Linux Installation Guide for Windows 10 for details on installing the WSL.

For completeness here’s the steps I took.

  • Run PowerShell as Admin and then execute the following

    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
    
  • Go to the Microsoft Store and type Run Linux on Windows into the search text box. In the store currently, there’s the options of Ubuntu, openSUSE Leap 42, SUSE Linux Enterprise Server 12, Debian and Kali Linux.

    Install your preferred distro – I went for Ubuntu simply because that’s what I have on most of my Linux boxes.

  • Once installed you’ll need to go to the command prompt | Properties and select the Options tab. From here ensure that Use legacy console (requires relaunch) is unchecked.

Once everything is initialized you’ll be in the user’s (whatever user name you created) home folder – on Windows this folder is stored in C:\Users\{username}\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home

We can access the C drive using

cd /mnt/c

Running the Linux from Windows

When you want to run the Windows Subsystem for Linux going forward, simply execute

wsl

Typescript interfaces

Interfaces (within Typescript) are used in a variety of ways.

Defining expectations/restrictions

We can define an interface such as IPoint

interface IPoint {
    x: number;
    y: number;
}

Just like other languages, we can then implement this interface and we’re required to duplicate the properties or methods declared on it.

For example here’s an implementation

class Point implements IPoint {
    constructor(public x: number, public y: number) {        
    }
}

We’ve used public constructor parameters hence implementing the IPoint interface.

Now we can create an instance of the Point like this

// p is a Point
var p = new Point(2, 3);
// p is declared as an IPoint
var p: IPoint = new Point(2, 3);

Within TypeScript an interface can also simply be thought of as shaping our data, so in this example we could create an anonymous class of type IPoint like this

var p: IPoint = {x : 2, y: 4};

We can also declare an instance of an anonymous type like this

var p = {x : 2, y: 4};

Typescript supports “duck typing” so we can create functions such as

function output(p: IPoint) {
   //
}

and we can pass a type which implements an IPoint or a duck typed anonymous type, for example

output({x : 2, y: 4});

Because of duck typing we do have an issue whereby the following two interfaces are structurally equivalent

interface IPoint {
    x: number;
    y: number;
}

interface I2d {
    x: number;
    y: number;
}

and hence this will compile and run

var i2d: I2d = {x : 2, y: 4};
var p: IPoint = i2d;

In such a case, the interfaces have the same properties and names and are structurally equivalent.

Things get a little more interesting with interfaces and anonymous types, in that we can write something like this

function whatIsThis() {
    return {x:2};
}

Obviously it’s not an IPoint or I2d, but it could be. In this example it’d create an anonymous type but we could cast the type like this, but this will fail to compile due to the missing y property.

function whatIsThis() {
    return <IPoint>{x:2};
}

Empty interfaces

We can also define empty interfaces, such as

interface IEmpty {}

function output(e: IEmpty) {
    console.log(e);
}

output("Hello World");

In this case this is equivalent to an any and hence does not cause a compile time error and output will display “Hello World”.

Type erasure

Interfaces within Typescript are erased at compile/transpile time, hence whilst they aid in ensuring our types are as expected, once compiled to JavaScript they’re removed.

Hence no runtime type checking exists for such types.

Listing environment variables

Occasionally we might wish to see a list of all the current environment variables.

Windows

In Windows, from the command prompt, execute

set | more 

or from Powershell

Get-ChildItem Env:

Linux

Within Linux, from the shell, execute

printenv

TypeScript constructor parameter properties

TypeScript offers a short-cut to creating properties/fields from the parameters declared on the constructor.

For example, if we declare our class and constructor like this

class Person {
    constructor(name: string, age: number) {
    }
}

let p = new Person("Scooby", 10);
p.name = "Doo";

The TypeScript transpiler will display the error “Property ‘name’ does not exist on type ‘Person'” and obviously the parameters name and age will not exist within the Person class as they’ve not been declared.

However if we prefix the parameters with either public, private, protected or readonly then TypeScript generates properties on the Person object automatically for us.

protected parameter properties

As you’d probably expect, with the accessor of protected properties are generated which are visible to the Person object and any subclass of the Person.

For example

class Person {
    constructor(protected name: string, protected age: number) {
    }
}

When we run this through the transpiler we get the following

var Person = /** @class */ (function () {
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    return Person;
}());

If we attempt to access the properties name and age from outside the class (using TypeScript) then we’ll get the error “Property ‘name’ is protected and only accessible within class ‘Person’ and its subclasses.”.

private parameter properties

If we now change the accessors to private, for example

class Person {
    constructor(private name: string, private age: number) {
    }
}

The transpiler will, again, create the same output as the previous JavaScript code, but the generated properties, from the TypeScript point of view, are only accessible from the Person class. Trying to access them from outside of the Person class will result in the following error, “Property ‘name’ is private and only accessible within class ‘Person’..

public parameter properties

Changing the accessors to public will, as you probably expected, create public properties/fields which are accessible outside of the Person class, here’s the altered source code.

class Person {
    constructor(public name: string, public age: number) {
    }
}

Ofcourse, the JavaScript code is unchanged.

readonly parameter properties

Finally, if we now change the accessors to readonly, for example

class Person {
    constructor(readonly name: string, readonly age: number) {
    }
}

The transpiler will generate, what appears to be, getters only. Hence trying to interact with these properties outside of the class will result in the following error “Cannot assign to ‘name’ because it is a read-only property.”

Whilst JavaScript can support the concept of readonly properties, the transpiler does not go this route (shown below)

Readonly properties in JavaScript

If we take the code generated by the transpiler, we could add the following

Object.defineProperty(Person.prototype, "name", {
    value: "name",
    writable: false
});

and when run (assuming we try to assign a value to name), we’ll get the following error “Cannot assign to read only property ‘name’ of object ‘#‘”.

React & Material UI “Invalid hook call. Hooks can only be called inside of the body of a function component”

When looking through some examples of writing Material UI code within React and using TypeScript (within a .tsx file to be precise) you might come across an error at runtime such as “Invalid hook call. Hooks can only be called inside of the body of a function component”.

Here’s an example of the code which causes this error

import React, { Component }  from "react";
import AddIcon from "@material-ui/icons/Add";
import { Fab } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
   fab: {
      margin: theme.spacing(1),
   },
}));

const classes = useStyles();

export default class SampleComponent extends Component<{}, {}> {
   public render() {
      return (
         <div>
            <Fab color="primary" aria-label="Add" className={classes.fab}><AddIcon /></Fab>
         </div>
       );
   }
}

What we need to do is wrap the useStyles code in a function and replace the code which uses it, so for example

const SampleFab = () => {
   const classes = useStyles();
   return <Fab color="primary" aria-label="Add" className={classes.fab}><AddIcon /></Fab>;
}

export default class SampleComponent extends Component<{}, {}> {
   public render() {
      return (
         <div>
            <SampleFab />
         </div>
      );
   }
}

This also shows how we can create reusable components from just a simple function.

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).