Typescript is javascript with type-checking

home

Typescript is microsoft's extension of javascript. Well, it's not really an extension -- all it does is let you add types and all that does is give you compile-time error messages. Obviously it won't run in a browser -- it's not one of those libraries written in javascript like jQuery. It's not a library at all. It's javascript with types added. I'd say it's one of those langauges which compiles to javascript, but the compile step is merely removing the types.

Strangely, Google's Angular framework recommends using it.

Adding explicit types

As a quick review, javascript has types. It has: string, number (not int or float, just number), boolean and the general-purpose object; plus values undefined and null. The weird thing about javascript is that, like Python, variables are typeless. Typescript allows you to "fix" that.

The syntax for declaring a type is a trailing colon followed by the type:

var n1 : number;
var n2 : number = 12;

var w : string = "frog";

// an array declaration:
var A : boolean[] = [true,false,false];

Functions work the same way, with the return value after the paramter block:

// original javascript function:
function addOne(n) { return n+1; }

function addOne(n:number):number { return n+1; }

A longer one finding the longest word in an array of strings, with declarations throughout. Note how this Typescript code is merely javscript with lots of type information added:

var A : string[] = ["cat","duck","xyooo","moo"];

function longestWord(A:string[]):string {
  let res:string = "";
  A.forEach(w=> { if(w.length>res.length) res=w;} );
  return res;
}

The benefit (if you enjoy typed variables) is getting more errors. The following is legal in javascript. Adding number in typescript turns it into the error it probably should be:

var n:number = 7
n="duck"; // legal in javascript, error in Typescript

Likewise it can check for valid operations, something javascript can only do runtime. Here Typescript catches how we can't multiply strings, compile-time:

var w : string = "abc";
var w2=w*2; // Typescript error -- strings have no *

In other words, adding types to javascript gives us all of the usual compile-time type-checking.

The any type

A problem with all of this type-checking is that some javavscript code relies on it's untyped variables. We might want n to be 3 or "cow". Or more commonly, we want those classic polymorphic functions: addOne, in regular javascript can take numbers or strings (it turns "abc" into "abc1").

To emulate regular typeless javascript variables, typescript gives us the any type:

var n:any = 7;
n="cow"; // this is just fine (in typescript)

// works on strings and numbers:
function tripleMe(n:any):any { return n+n+n; }

// works on array of anything:
function numCopies(N:any[], n:any):number {
  let count:number=0;
  for(let i:number=0; i<N.length; i++)
    if(N[i]==n) count++;
  return count;
}

Typescript also adds a similar type which is like the traditional OOP Object base class -- it can be anything, but can't be used for anything (until it's cast to something real). They named it unknown:

Handling missing parameters

Javascript has a crazy rule that you don't need to supply all of the arguements to a function. The ones you skip are undefined (which is a completely valid value). Typescript forces you to mark them as optional parameters, using an ?:

// optional second parameter:
function abc(n1:number, n2?:number):number {
  if(n2==undefined) return n1*10;
  else return n1+n2;
}

n=abc(6); // 60
n=abc(4,7); // 11

Note that in regular javascript you don't do anything special. The function is written abc(n1, n2). Technically n1 is also optional -- but we have no way of knowing which missing parms the function can handle. But that's mostly pointless since javascript has perfectly good default parameters. There's no reason not to use them, so no reason to ever need this ? rule. Function abc could be written without question marks, more-or-less as:

function abc(n1:number, n2:number=0):number {
  if(n2==0) return n1*10;
  else return n1+n2;
}

Implicit types

If you start up a TypeScript environment and drop in your javascript it will add types, whether you want it to or not. For example this legal javascript code gets number added, creating a typescript error:

// typescript error -- n implictly declared as number:
var n=6; // implicitly declares n as number
n="moo"; // error (in Typescript, not in javascript)

This example from before, with no types added, is also a compile-time error since w is implicitly declared as a string:

var w="abc";
var w2=w*2; // error -- strings have no *

For fun, the result in javascript is w2=NaN (and no error). Maybe we're happy with that, but probably not. Typescript is probably helping us out here.

A fun note, we can get around implicit types by declaring the variable by itself. Typescript will assume it's any. This is legal:

var x; // implicitly declared as "any"
x=8;
x="truck";

Function parameters also default to any, but typescript will yell at you to manually add a real type.

Declared structs

Recall that in javascipt a function taking a Cat class as input has a heading like this: function useCat(c1). Oh, right. In javascript we can't declare, or enforce, parameter types. Typescript wants to fix that. Instead of somehow leveraging off of javascripts class features, typescript defines its own in a way more in keeping with javascript.

A typescript class type can be created with the interface keyword. List the required fields and types. It looks a little like creating an object, but note the lack of quotes around the name. This makes a type that says "must have a numeric n field":

interface Num_t { n:number; }

We can now use Num_t as a type:

// n1 declared as object with an n:
var n1:Num_t {"n":8};

// function requires any object with an n field:
function doStuff(x:Num_t) number {
  return x.n*2;
}

doStuff(n1); // fine
doStuff(7); // compile error (not even an object!)
doStuff( {"s":7} ); // compile error -- no n field

As a bonus, declaring as an interface prevents you from adding extra fields:

// warns about and removes cats:
var n3:Num_t = {"cats":"meio", "n":0};
var z = n3.cats; // error -- no such field cats
n3.x=8; // error -- Num_t has no x (prevented from adding)

Again, just as a reminder, javascript objects can have new fields added to them, willy-nilly. This even includes objects using the formal class syntax. Typescript is enforcing something here that javascript never did.

Typescript interfaces work in typical loose fashion -- users can have more than what they require. Here, an extra cats field is fine (which we were able to legally add since n2 has type any:

var n2:any {"cats":"meio", "n":0};
doStuff(n2); // just fine

Anonymous interfaces

If you're not in the mood for all of that, you're allowed to put an anonymous interface right where the type goes:

// requires input to have field "n" (same as n:Num_t):
function useN(val:{n:number}) { return val.n*10; }

// requires the input to have "title" and "title2" fields:
function makeName(val:{title:string, title2:string}) {
  return val.title + " john " + val.title2;
}

This trick can even be used in variable declarations (but I don't see any use):

let p2:{x:number; y:number} = {"x":3, "y":7}

Alternate Typedef syntax

If you have the word interface, you can use an alternate syntax. Note the new =-sign:

type Point = {x:number; y:number};
// same as: interface Point {x:number; y:number};

// use Point as normal:
let n3:Point;
function(p1:Point, p2:Point):Point { ...

All 3 ways (interface, anonymous, type) amount to the same thing, and mix freely.

requiring member functions

interfaces can also require functions, using a mostly normal function signature syntax:

interface fHaver {
  f1:(x:number) => number;
  f2:(w:string) => void;
}

It's used the normal way. Here we create fHaver frog (with functions made using 2 different syntaxes, for fun). It doesn't need any type annotations since the system knows what they must be:

var frog : fHaver = {
  "f1":function(n) { return n*2; },
  "f2": (w) => { console.log("["+w+"]"); }
}

An example of a very silly function requiring and using a fHaver input:

function fHaverUser(f : fHaver):void {
  f.f2( "num="+f.f1(6));
}

fHaverUser(frog); // [num=12]

And of course we can create interfaces with both fields and functions:

interface nameAge {
  name : string;
  age : number;
  info : () => string;
}

Combining interfaces

This is ugly, but interfaces can be combined using &. This (useless) function requires inputs which are both Num_t's and fHaver's. The important thing is it uses val.n, val.f1 and val.f2:

function return largerAt0(val: Num_t & fHaver) : void {
  if(val.f1(val.n) >= 0) val.f2(0);
  val.f2(val.n);
}

The same trick works in a variable declararion. This combined "needs an x field" and "needs a y field":

interface xxx { x:number };
interface yyy { y:number };

// xy must have both:
var xy: xxx & yyy = {"x":5, "y":9};

And, of course, we'd probably create a new type for this:

type Point2 = xxx & yyy;

var p : Point2; // will assign later

Combined types

Pretty much the entire point of typescript is that we don't like how javascript's variables can be any type, any time. The any keyword is a last resort. When we must, we'd much rather declare a variable as being one of a few specific types. That's what the pipe (|) type combiner is for. Here we can declare a "type" as a number or string, but nothing else:

var x: number | string;
x=8;
x="cat";
x=true; // error -- only number or string

// number or string input (but nothing else):
function thing( s:(number|string) ):string {
  return "a"+s;
}

This can mix pretty much anything, for example, imagine a sloppy javascript function wanting an array of numbers, but also taking single numbers:

function largest(n: number | number[]) : number {
  if(typeof n == "number") return n;
  ...
}

These thing1|thing2 types do not seem to play nicely with any (which may be the point?)

Nullable types

As a quick review, since javascript variables can be anything, they can always be null. Typescript decided they can't be null unless you specifically provide for it with the combined type syntax:

var w : string = null; // error
var w2 : string|null;
w2="cat";
w2=null; // this is fine

var N: number[] | null;
N = null;
N = [4,7,2];

To really emulate javascript you may have to also use undefined, giving things like string|null|undefined. This is probably often used with the type shortcut:

type stringOrBad = string|null|undefined;

var w:stringOrBad; // using our new type

// do something that would make it undefined:
var x = {};
w = x.notFound; // fine -- w is legally "undefined"

enumerated types

This is just weird. | can also be used to list all possible values, giving a sort of enumerated type. Here catNum variables can be one of 4 numbers, and stage_t one of 3 strings:

type catNums = 1|4|7|12;
var x2:catNums=3; // error

type stage_t = "begin"|"waiting"|"falling";
let w:stage_t = "waiting";

Here's a fun use in an interface, restricting the direction to -1, 0 or 1:

interface moveAmt { amt:number, dir:-1|0|1};

var m1: moveAmt = {"amt":6.7, "dir":1} // "dir":3 is an error

Or using it directly as a parameter type:

function goBy(dir:-1|0|1) { ... }
goBy(-1); // legal
goBy(4); // error

Of course, if you want to specify 1 to 100, you're out of luck.

null-checking

It's become popular to have compilers check for links which might be null. Typescript also adds this, but only for types which you've said can be null. This establishes n.x could be null, then attempts to use it:

// x field can be null:
interface xHaver { x:number|null };

function nullCheck(n : xHaver) {
  if(n.x<3) ...

Typescript gives us an error (on newer versions) "x.n could be null". One fix is the usual -- check it. Typescripts static compilers notices the check and is happy:

  // typescript likes this:
  if(n.x!=null && n.x<3) ...

You also get the standard bang(!) to suppress could-be-null errors. It doesn't actually do anything (but then again, nothing in typescript does anything), just prevents the error:

  // typescript also likes this:
  if(n.x!<3) ...

alternate array syntax

A little known fact is that you can use the keyword Array to create arrays in regular javascript:

var A1 = [1,2,4,6]; // normal way to make an array
var A2 = new Array(1,2,4,6); // odd, but the same thing

Typescript somewhat mirrors this. string[] is the usual way to declare an array of strings. But you can also write Array<string>. I suppose that feels more natural for former C#-users (which is also a microsoft language).

Conclusion

Javacript is the only thing that runs on a client's web page, so we need to write it somehow. For people who don't want to learn it, we've got languages which compile into javascript. Microsoft's own ASP.net even allowed you to write eventual client-side code using their C#. On the other hand, there are plenty of people who can easily learn it -- Python users for example -- and who think javascript's system of untyped variables is fine.

In theory, one of typescript's big advantages is allowing native javascript users the ability to add type information. But javascript users don't want that. The other potential advantage is allowing coders who want type information to have it, while also using real javascript. But I don't think they want that, either -- they'd be happier in a more conventional language compiling into js.

It seems like one of those typical microsoft things that everyone else knew was doomed. First they assumed we forgot to add typed-variables to javascript. No. That's like saying your icecream cone is missing the bun. Then they assumed they could slap a few types and fix it. Again, no. Computer languages have different philosophies -- they're aren't all inferior versions of <insert the first language you learned>. And who would have guessed adding typed variables to a language that doesn't want them would blow up in complexity? Everyone. It's nice how a smart editor can use those type hints nicely; but to paraphrase Jesse Ventura's character in "Running Man", back in the day we were able to write javascript just fine without it.

 

 

 

Comments. or email adminATtaxesforcatses.com