Over the last twenty years, software has moved towards the web and has made it easier for those of us who prefer not to work with Windows. But I frequently felt challenged, when suggesting in a business that I'd be happy to run on Linux instead. More than once did it mean that some systems that I had to use were not available to me. In addition I love the dbForge Studio family of database IDEs which are only available on Windows. At the same time, I never warmed to the GUI side of Linux. Gnome, Kde, Unity, not to mention the dire choice of applications, all left me wanting for more.

Prior to 2016, a lot of my development took place either on my own Linux laptop or using a cloudbased IDE (such as Codeanywhere) so that I could work natively under Linux from a Windows 10 laptop when at work. When Microsoft announced Windows Subsystem for Linux, I was really excited.

I installed it on the Insider build as soon as I could and the system never disappointed. There were some niggles, but having the same OS for development on my Windows laptop as I had on some VPS or container was just incredible. VSCode, running on Windows but talking directly to WSL and the bash, has led to this becoming my IDE of choice.

It's been a while since I created a new Javascript project in this setup and even when I did, I used Meteor, which does not really use the typical Javascript (or Typescript) configuration. As I am currently getting my head around Typescript, I felt it would be beneficial to take some notes on the configuration.

Prerequisites

I'm assuming that VSCode is installed and running on Windows but als with the WSL extension. And that node and npm are installed on Ubuntu running on the WSL subsystem.

To install typescript you simply need to run:

npm install -g typescript

Creating a minimal TypeScript project

I've created a minimial typescript project in a directory called VSCodeBoilerplate and open it with VSCode:

mkdir VSCodeBoilerplate 
cd VSCodeBoilerplate
tsc --init
code .

In VSCode I now create one more file called 'app.ts' with the below content.

class SimpleDemo {
  constructor(public message: string) { }
}

var demo = new SimpleDemo('This is to show how to setup VSCode with Typescript.');
console.log(demo.message);

Creating a Build task

Now what I've created the above class I want to be able to build the javascript project. The VSCode Build tasks have changed and there is some outdated information. So even though I won't use them later, I wanted to create a sample of a build task using the version 2.0.0 of the tasks.json configuration.

To create this file for the first time select Ctrl + Shift + B. VScode will create a dropdown and next to the build task you can see a little cog wheel for the settings. Click it and it will create a file called tasks.json. I usually prefer to run building in a watch state so I have amended this as per the below:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "typescript",
            "tsconfig": "tsconfig.json",
            "problemMatcher": [
                "$tsc-watch"
            ],
            "group": "build",
            "label": "tsc: watch - tsconfig.json"
        }
    ]
}

The above code essentially defer's the building to the tsconfig.json. But it can be used to trigger this process using the Ctrl + Shift + B key combination.

Refining the TypeScript Configuration

Up until now we had created boilerplate tsconfig.json file through the tsc --init command. It's time to look at this file. I've linked to the documentation below, but for this project I reviewed the parameters that were already set as well as anything set in the jsynowiec/node-typescript-boilerplate project on github, that I also referenced below. Here is what I have changed:

  • compilerOptions.target: This value defaults to es3. As most modern browsers support es6 and because I never actively worked with JS prior to 2015, I've opted to use this version which means that if I wonder how typescript is translated to JS, I've got a chance to better understand what I see.
  • compilerOptions.outDir: By changing this parameter to build/ all json files are being moved to a folder called build.
  • include: Similar to above I will leave any settings in the root directory but move ts files into a folder called src. To include all files in src and it's subdirectories I've changed include to ["src/**/*"].
  • compilerOptions.forceConsistentCasingInFileNames: I'll set this to true as I do not want issues with duplication.
  • compilerOptions.noImplicitReturns: I've set this to true as I prefer this style of code. I've adopted the same for noUnusedLocals and noUnusedParameters
  • exclude: I'm excluding the typical files here: [ "node_modules", "**/*.spec.ts"]

Creating an NPM Run task

I don't tend to use VSCode build commands as much as I could. Instead I keep the linux terminal open in my VSCode window and I am much more likely to use npm commands in there to run what I want to run. So I'll be setting the scripts section of my project up to initually create the npm run dev script which will run my package each time I make changes. To do this we will first need to install the nodemon package and then create the default package.json file. I'm just using the legacy init here.

npm install nodemon
npm --init -y

Next I'm updating my package.json file to include the following section:

{  ...
   "scripts" {
     "compile": "tsc && node build/app.js",
     "dev": "./node_modules/nodemon/bin/nodemon.js -e ts  --exec \"npm run compile\"",
  }, 
   ...
}

I have created two commands:

  • compile which just compiles the typescript sourcecode and runs the reslting javascript in node.
  • dev which triggers compile each time my code has changed.

I can now simply develop and see the latest outputs running npm run dev.

Linting with ESLint

VSCode has some default syntax checking for TypeScript enabled, but I frankly don't like it. It's way to permissive and unspecific. Just one of the examples is that it does not care about explicit or implicit semicolons to terminate a line of code. And there really is no reason not to use ESLint instead.

So we first need to install the VSCode extension ESLint. (Note that the extension TSLint has been deprecated). Next we need to install eslint, globally and then it dependencies locally within the project.

npm i -g eslint
cd [project]
npm i -D eslint
npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm i -D eslint-config-airbnb-typescript eslint-plugin-import
npm i -D eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks
npm i -D typescript

I'm using a bunch of dependencies here in preparation of what I want to do later with this project but notable dependencies are:

  • Typescript: This needs to be installed as a dev dependency for this project.
  • Airbnb config: I used to use the Airbnb config in the past and plan on using this here as a configuration basline.

Finally I configure my .eslintrc.json file like:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": [
      "./tsconfig.json"
    ]
  },
  "plugins": [
    "@typescript-eslint"
  ],
  "extends": [
    "airbnb-typescript/base"
  ],
  "rules": {}
}

I can now run my linter using eslint src --ext .ts and see the following output:

/home/hans/dev/typescript/VSCodeBoilerplate/src/app.ts
   7:5  error    'message' is never reassigned. Use 'const' instead         prefer-const
   9:1  warning  Unexpected console statement                               no-console
  10:1  error    Too many blank lines at the end of file. Max of 0 allowed  no-multiple-empty-lines

✖ 3 problems (2 errors, 1 warning)
  2 errors and 0 warnings potentially fixable with the `--fix` option.

I'll fix these with the --fix option and leave the problem active as I don't have plans to leave the code running. I have now configured ESLint but not yet the integration of ESLint with VSCode. To amend this I need to open the eslint code extension settings (Ctrl + Shift + P then Preferences: Open Settings (JSON))

{
  "eslint.validate": [
     "typescript",
     "typescriptreact",
  ],
  "eslint.autoFixOnSave": true
}

If I go back to my shell however I would now expect highlighting of the console.log(demo.message) statement as the Airbnb settings don't allow the use of console. Only the class in the first line is highlighted with an error message that VSCode needs execution rights. I click Fix this and allow this everywhere and now, as I would expect the problematic line is highlighted and the problem explained in the 'problems' section

That's my setup for now. The final code can be found in this github repo.

Sources and References: