Practical TypeScript Migration – TypeScript Migration Toolbox and Workflow

SteveO

Steve Ognibene is your TypeScript tutor

Welcome to Part 3 of this review of the Pluralsight course Practical TypeScript Migration by Steve Ognibene.

Steve has been doing professional web development since the year 2000, and specializes in SQL Server, C#, VB.NET and TypeScript.

He loves teaching and talking about all of the technology platforms he uses, and is an active contributor to open source projects.

Also in this series:

Part 1 – Introduction

Part 2 – The First Conversion

This episode looks at the third module form this course

TypeScript Migration Toolbox and Workflow

Reimplementing Coins as a Class

Steve create a new file coins.ts and moves the coins array declaration to here.

TypeScript isn’t happy with Big.js so Steve uses Nuget to download big.js.TypeScript.DefinitelyTyped, and this fixes all the errors.

coins is an array of objects, and each of the objects has the following properties:

  • name (e.g. “Penny”)
  • value (e.g. Big(‘0.01’))
  • imgSrc (e.g. “penny.png”)
  • count (e.g. ko.observable(0))
  • max  (e.g. ko.observable(10))

In coins.ts, we want to refactor our code to eliminate duplication.

Steve adds a new class Coin with a constructor.

The constructor has arguments for the name, style and value properties that exist in the coins array.

We use the public keyword on each constructor property, and also specify the type of each property.

We know that name and style will have the string type, but what about value?

By hovering over value in our original code, TypeScript tells us that the type is BigJsLibrary.BigJS

TypeScript can’t always know what type this refers to (although I understand that there have been improvements made here since the recording). However, inside our constructor, TypeScript knows that this refers to the class.

Using our new class, our coins definition is much more terse.

We run it up to test that it works, and see the runtime error :

“Object doesn’t support this action”

What this obscure message is trying to tell us is we are trying to use Coin before it has been assigned its value. The undefined variable Coin doesn’t support the action of calling new on it.

Fortunately this is an easy fix, and now everything works.

Migrating CoinCounterViewModel as a Function

Almost as soon as we rename to the TypeScript extension we get 27 errors.

First off, we have:

Cannot find name ‘$’.

To fix this, go to Nuget and install jquery.TypeScript.DefinitelyTyped

Using Visual Commander, we perform the Quick Reload action, and get down to 18 errors.

Most of these are of the form:

Parameter ‘x’  implictly has an ‘any’ type

For callback, Steve defines it as

callback: (viewModel: typeof self) => void

For coin, we simply give it the Coin type.
For coinName, it is a string
zeroBasedIndex is a number

We have errors for coin.addCoinEnabled and coin.removeCoinEnabled

TypeScript really doesn’t like you adding things to an object that it doesn’t expect, and neither of these properties have been previously defined. Steve adds them as properties of our Coin class.

Okay down to 5 errors:

Property ‘modal’ does not exist on type ‘jQuery’

This refers to our Bootstrap modal. We fix this by installing bootstrap.TypeScript.DefinitelyTyped.

Type ‘string’ is not assignable to type Location.

We should be assigning our string value to Location.href, rather than Location.

Steve says using TypeScript makes you wonder how your JavaScript worked at all before.

With all errors fixed, we see that the game still works well.

Converting CoinCounterViewModel to a Class

Steve renames gametests.js to gametests.ts and reloads the project. Only one distinct errors this time:

‘new’ expression, whose target lacks a construct signature, implicitly has an ‘any’ type

What this means is CoinCounterViewModel isn’t a class even though we are calling new on it. Rather than eliminating this error using the “light touch” interface technique that we saw before, we convert CoinCounterViewModel to a TypeScript class.

This is several minutes of work, and after we clear all the compiler errors we get one at run time:

Error: Unable to get property ‘isRunning’ of undefined or null reference. Steve says we need to use a lambda, and that we’re probably wondering what the hell he’s going on about.

Lambdas and How ‘this’ Works in TypeScript

The way that this works in JavaScript is often confusing.

It is contextually bound – it depends on how you call the code that contains it.

With this hopefully understood, Steve explains what a Lambda (or “Arrtow function”) is in JavaScript, and how they differ from standard functions.

When you’re using standard function calls, this in TypeScript works identically to how this works in JavaScript.

But when we use a lambda, TypeScript automatically emits some extra JavaScript to capture this from the scope where the lambda is declared.

If you want your this to be contextually bound (i.e. normal JavaScript), use the function keyword.

If you want your this to be lexically bound, use a lambda.

Don’t fall into the trap of thinking lambdas are just syntactic sugar. They work differently.

Steve rewrites the functions as lambdas, clears down the errors, and the emitted javascript includes:

var _this = this;

Completing the Migration and Internal Modules

Okay that was a lot of work, but now we’re on the home straight.

We’re going to do our final migration and refactoring:

  • Remove JavaScript from index.html
  • Implement “CoinCounter” module
  • Improve confusion of concerns with Coin

We cut our $(document).ready block of code out of our html file and paste it in index.ts. This just works.

In app.ts we embed all of code in a new module:

module CoinCounter { … }

Steve explains that we must remember to add the export keyword to var app. This pattern is also applied to other files.

When we run up our app we see another run time error:

Error: ‘spacesToUnderscore’ is undefined

We find this code is part of a data-bind attribute value in index.html – this is not TypeScript code so TypeScript cannot understand it’s type. Steve refactors this to call our TypeScript function instead.

TypeScript Internal Modules vs. External Modules

Many people find the term internal modules confusing.

Steve discusses the Github thread on renaming from internal modules to namespaces.

External modules are now just called modules, and Steve likes to think of them as File-based modules.

The two flavours of modules are Common JS and AMD and Steve describes both of these systems here.

Check out one the following courses to learn more:

Steve says he hears a lot of developers asking “What Style of Modules Should I Choose?”

We are advised that our aim is to keep the project complexity down to a minimum. Namespaces work well for low to medium complexity projects.

Modules help automate interdependency resolution, but they introduce problems of their own, especially when using TypeScript.

In this clip we see a chart to help us make the right choice for our project.

This is entirely consistent with my experience of modules and namespaces and makes me feel better about using namespaces for so long when some developers say that everyone should be using modules in JavaScript.

That said, due to the ES2015 effect, modules are becoming more and more common.

Separating Coin and ViewModel Concerns

Steve removes the body of the Qunit tests and rename contests.js to cointests.ts.

We see each test created using the Red Green Refactor pattern of test driven development.

If you are new to Test Driven Development, or would like to learn it in even more detail, see Learning Unit Testing and Test Driven Development.

We see all tests are passing, and our manual testing works well too.

Extracting a Class from CoinCounterViewModel

Steve creates a new file called HighScore.ts and moves the starterHighScoreList code from app.ts into here.

He moves the interface into this class, and exports it so that is can also be used elsewhere.

We have some more problems, and Steve says while TypeScript does its best, occasionally APIs have places where it’s easy to make a mistake and TypeScript can’t help you because the code you wrote is technically valid.

In the final episode of this series, we’ll see how we can take advantage of some free tools to improve our website’s performance, and make our TypeScript development experience even better.

Part 4 is coming soon

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s