Practical TypeScript

More Cool Stuff in TypeScript 2.0 and 2.1

A roundup of a few more features: deprecated any type, literal datatypes, read-only properties, more!

In previous columns I've covered some of the new features in TypeScript 2.0. I looked at eliminating nulls/undefined values, leveraging class discriminants, and exploiting private constructors and building class hierarchies using abstract classes and members. This column isn't as organized as those columns: It's a roundup of what I think is new and cool in TypeScript 2.0 that I haven't already covered. I'll also throw in what's new in TypeScript 2.1.

Less any
In fact, my favorite remaining feature is part of TypeScript 2.1: The deprecation of the any datatype.

This matters because, if you use TypeScript then it's because you value data typing and type safety. Certainly it's the reason I use TypeScript. Not surprisingly, then, I hate the default any type that TypeScript assigns to any variable not declared with a datatype. I prefer to compile my TypeScript code using the noImplicitAny option, which forbids the compiler from assigning the any type to any variable unless I explicitly declare the variable with the any datatype.

Unfortunately, using this option makes it harder to port JavaScript code to TypeScript. For example, it's not unusual to have JavaScript code like this:

var counter;
//...later in the code...
counter = 1;

Without noImplicitAny, the compiler assigns the datatype any to the variable counter … which makes me unhappy. But with the noImplicitAny option turned on, the way counter is declared will generate a compile-time error, which makes it impossible to run my code. To eliminate those errors, I either have to give up noImplicitAny (and accept the any type on the counter variable) or rewrite the declaration of counter (and probably a lot of other ported code) to specify a datatype.

Fortunately, TypeScript 2.1 eliminates this problem with its improved code-flow analysis. In TypeScript 2.1, the compiler will see that the counter variable is, eventually, assigned the number one. Based on that assignment, TypeScript 2.1 will assign the counter variable the datatype number, rather than any. In other words, you get to avoid the any datatype and you don't have to rewrite as much (or any) existing JavaScript code.

This also means that you can skip assigning datatypes when declaring variables and just use some assignment statement later in your code to specify the datatype … but I think you're missing the point of TypeScript if you do that.

Assigning Datatype with Literals
TypeScript will also, now, assign a datatype if a variable has its value set using a literal in the variable's declaration.

If the variable is declared using the const keyword, the datatype of the variable is set to the value assigned. For example, in the following code, the datatype of base is 4:

const base = 4

If you use the var or let keywords, the datatype for the variable is set to the datatype of the literal. In the following code, the datatype of base is number:

let base = 4

For all three of const, let and var, if the value assigned to the variable has multiple datatypes, then the datatype will be a union of the various types. In the following code, the datatype of terminator will be the union boolean | number:

let root = base < 0 ? false : Math.sqrt(base);

Read-Only Properties
When defining classes you can now declare fields using the new readonly attribute, which means the field can only be set from the constructor. In the following Order class, for example, while the customerId and orderDate properties can be set by code using the object, the id property can only be set from the Order class' constructor because the field is marked with the readonly keyword:

class Order {
  public readonly id: string;
  public customerId: string;
  public orderDate: Date;

  public constructor(id: string) {
    this.id = id
  }

TypeScript has always supported creating properties from the parameters passed to the constructor and readonly can be used there also. I can condense the previous code into this, if I wish:

class Order {
  public customerId: string;
  public orderDate: Date;

  public constructor(readonly id: string) {  }

I can also use optional parameters to define the customerId and orderDate properties in my constructor. This lets me add new parameters to my Order class's constructor without breaking any existing client that's just passing an id value to the Order constructor. Optional parameters are marked with a question mark. The following code specifies that customerId and orderDate are optional:

class Order {
  public constructor(readonly id: string,
                      customerId?: string,
                      orderDate?: Date) {  }

Specifying the DataType for This
Within a method you can specify the datatype to be used with this by specifying this as a parameter to your method and assigning it a datatype. In the following code, this has been specified as being an HTMLButtonElement:

Delete(this: HTMLButtonElement) {
      
  }

The this parameter must be the first parameter to the method and isn't visible to the calling code. Client code calling this method would use this:

Delete();

Declaring this as void means that the compiler won't let you use this within the method.

Be aware: Specifying a datatype for this gives you IntelliSense support when you use this inside the method but the datatype you assign has no impact at runtime. If, when the method executes, this has some other datatype … well, at the very least, your code probably won't execute as you expected.

More Compiler Options
There are also some additional options that you can turn on so that the compiler will protect you from yourself. Two are easy to describe:

  • noUnusedParameters will, at compile time, flag any parameter to a function that's not used in the function
  • noUnusedLocals does the same thing for variables declared and never used

On the other hand, the strictNullChecks option requires some more explanation. With strictNullChecks turned on, you must assign a value to a variable before you can use it. In addition, the values null and undefined can only be used with variables declared as any.

There are, of course, exceptions. Optional parameters, regardless of what datatype they're declared with, will be union types that include undefined. With union types that include null/undefined, the compiler will recognize type guards that exclude null/undefined. As a result, inside the guard's code block, the variable will be treated as if it didn't support null/undefined.

For example, in my earlier code for the Order class's constructor, I specified the datatype of an optional parameter called customerId as string. Because I flagged the parameter as optional, the compiler will assign customerId a datatype of the union string | undefined.

Within the following if block (which acts as a type guard eliminating nulls), the compiler will assume that customerId is datatyped just as string:

if (customerId) {
  //...customerId as string...
    }

By the way, type guards also now work with properties. This code instantiates my Customer class and then uses a type guard to ensure that customerId is a string inside the code block:

var cust = new Customer("A123");
if (cust.customerId) {
  //...customerId as string...
    }

That's a lot of new features to absorb, but, for me, the most important ones are the ones that check my code. As a result, here's what the start of my tsconfig.json file now looks like:

"compilerOptions": {
  "noImplicitAny": true,
  "strictNullChecks": true,
  "noImplicitReturns": true,
  "noImplicitThis": true,
  "noUnusedLocals": true,
  "noUnusedParameters": true,

As you can see, I try to get the compiler to check as much as I can. (That might give you some idea of the confidence I have in my own programming abilities.)

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.