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
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 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 ‘#
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 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) { }
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
{ "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" ] }
class App { getSalutation() { return "Hello World" } } let app = new App(); console.log(app.getSalutation());
"scripts": { "build": "tsc --watch", "start": "babel-node --presets es2015 ./public/index.js" }
{ "presets": ["env"] }
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" } };
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
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
Adding webpack
Next up we’re going to create our basic web site, extending on what we’ve already covered.
<html> <body> <script src="./index.js"></script> </body> </html>
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"] } } } ] } };
document.body.innerHTML = app.getSalutation();
Time to create bundles
We’re going to use webpack to create some distribution bundles
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: '/' } };
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.
const MinifyPlugin = require("babel-minify-webpack-plugin");
plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Demo' }), new MinifyPlugin() ],
Trying an alternate minify
Let’s try terser
const TerserPlugin = require('terser-webpack-plugin');
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.
Assuming you already have a project set-up. If you don’t have typescript within your node modules, add it using
Next up we’ll add eslint and it’s typescript plugins (to the dev dependencies)
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
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.
'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; }
yarn init --yes
{ "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 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.
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 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 }