Welcome to Part 2 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 2- The First Conversion
Part 3 – TypeScript Migration Toolbox and Workflow
The First Conversion
A Tour of CoinCounter
We take a look at the application that we’ll be migrating to TypeScript.
It’s a simple game to help children to count change. The game asks the user to make up a random number of cents using pennies, nickels, dimes and quarters, all against the clock.
Steve shows us that there are 26 QUnit tests for this game.
We learn that this game is coded using the following technologies:
Steve describes the project structure, and this is fairly typical for a small web app. We also see the code for many of the functions in the program.
Choosing a Candidate for Conversion
Steve recommends starting your migration with a function that is short and simple. Get a small win before attempting to convert any big and complex functions.
Prefer code that is covered by tests, because if you need to refactor then those tests will provides you with a safety net against regression.
Other desirable characteristics for conversion and also discussed.
We see a conversion worksheet featuring lines of code, complexity, how much it’s referenced and notes on tests.
The First Extract and Conversion
We’re starting our migration with the app variable, and we extract this code to a new file app.js, add a new reference to it in our HTML files and confirm everything is still working.
With this confirmed, Steve renames it to app.ts and views it in Visual Studio with Web Essentials.
Steve mentions a Visual Studio “Reload Project” Bug in TypeScript 1.3 and 1.4.
I believe that this issue is now fixed, but it’s good to learn about Sergey Vlasov’s Visual Commander Extension which is potentially useful for much more than just eliminating the bug.
For details on this see Steve’s QuickReload Gist. Steve demonstrates exactly how to use this.
Visual Studio TypeScript Build Properties
A TypeScript Build tab is added to Visual Studio Project Properties, and Steve discusses the most useful settings here.
Steve says the ECMAScript 6 option will probably be good in a year or so. We it’s now a year or so since he originally said this and he was right.
There’s a checkbox for allow implicit ‘any’ types – this requires an understanding of type inference so Steve explains that here.
This is very beginner friendly so even if you’ve never seen TypeScript before you should have no problem following along.
Steve recommends disallowing implicit ‘any’ types.
To learn about module systems I recommend one or both of Jeff Valore’s courses, or Joe Eames’s course:
We see that Generate source maps is one of the debugging options.
Preparing Your Team for TypeScript
Steve talks about the practicalities, warning that the version of TypeScript you use is somewhat strongly tied to your version of Visual Studio.
Having all developers on your team on the same version and update is a best practice regardless of whether you are using TypeScript.
We see a table of versions of TypeScript and Visual Studio.
At the time of my writing, the latest version of TypeScript is 1.8. To learn more about the latest TypeScript features, see What’s New in TypeScript by the legendary Anders Hejlsberg, or read Announcing TypeScript 1.8.
I recommend using the latest version almost without exception. It’s possible that a newer version will break something that you need to spend a bit of time fxing, but that’s something that will need to be done sooner or later and probably won’t get any easier if it’s done later.
I also recommend that your team uses the latest update for whichever version of Visual Studio you are using.
Addressing Team Member Opposition
A couple of the more reasonable arguments against TypeScript are:
- I don’t want to wait for a compile step
- I don’t want to fight with a type checker
Steve provides guidance on keeping these team members happy. This is good advice and reminds me of Erik Dietrich’s course Making The Business Case For Best Practices, which made my Pluralsight Top 10.
Now we attempt to convert a big function, and we see the red squigglys. We have 7 errors:
- Parameter ‘initialTimeInSeconds’ implicitly has an ‘any’ type.
- Parameter ‘callbackOnClockElapsed’ implicitly has an ‘any’ type.
- Variable ‘intervalHandle’ implicitly has an ‘any’ type.
- Cannot find name ‘ko’
- Cannot find name ‘ko’
- Cannot find name ‘ko’
- Parameter ‘numberOfSeconds’ implicitly has an ‘any’ type.
It’s all gone wrong! What do we do?
This for me is the heart of the course: overcoming these problems and appreciating how TypeScript works and helps us.
Steve shows us lib.d.ts and explains what is declares.
Next we see how to declare the knockout ko object in TypeScript.
We are also introduced to DefinitelyTyped, a huge project with over 10,000 commits and almost 2000 files.
Steve explains the role that Boris Yankov had in this project and how the project exploded in popularity.
We also see another bug with the TypeScript plugin for Visual Studio, and how to work around it.
Using Our Converted GameClock
Up until now this course has been very easy. This clip is much more densely information packed, so if you’ve been watching a high speed, either reduce the playback speed or make use of the pause button here.
After we have downloaded the QUnit definition file, we get 11 compiler errors. These are only 3 distinct errors, and they relate to QUnit:
Supplied parameters do not match any signature of call target
We get this on each line wherever we new up GameClock with a number parameter.
Our function definition has two parameters, but the second one is not marked as optional.
This can be done by adding question marks to the end each parameter name e.g. initialTimeInSeconds?
‘new’ expression, whose target lacks a construct signature, implicitly has an ‘any’ type
In our TypeScript settings we do not have the allow implicit ‘any’ types setting enabled. So the laziest way to remove these errors is to turn it on.
An alternative is to wrap each like so:
Now this reduces rather than increases your codes signal to noise ratio.
The best approach is to provide TypeScript with an explicit signature for an instantiated GameClock. There are two ways to achieve this:
- Annotate with an interface
- Refactor as a class
Steve explains that refactoring as a class requires more upfront changes, but is arguably more maintainable and semantic.
If we decide to annotate with an interface, it means we need to manually keep the interface in sync with the implementation. However it’s faster to get started with almost no up front changes.
Adding TypeScript Interfaces
Beginning at (3:40) in this video clip, Steve creates an empty interface called GameClock and sets the GameClock function as an implementation of it. Now we see
Cannot use ‘new’ with an expression that lacks a call on construct signature
Steve adds copies our GameClock function signature into our interface definition and prepends it with the new keyword.
Again we trade one error for another:
Property does not exist on type
Steve shows that we can fix this error by adding the property to the interface definition. However, we find that our property isn’t yet strongly typed. With normal (non-lambda) TypeScript functions, the variable is explicitly of type any.
In the GameClock function, we are capturing the variable this into the variable self.
TypeScript does not automatically know the type of this, but we can explicitly define it to be our GameClock type.
Steve uses Visual Studio’s split screen feature to edit the interface and the GameClock function without constant scrolling up and down.
We see that TypeScript can be overzealous with its type checking. We can make these errors disappear by adding <any>, but Steve describes this practice as a junk drawer, and introduces us to TypeScript 1.4’s name types feature:
type ClassFunction = any;
The effect is the same, but some semantic meaning has now been applied.
The next episode will be exploring lambdas, classes and modules in TypeScript.
Continue to Part 3 – TypeScript Migration Toolbox and Workflow