Minimize the price (not the benefit) of type in Typescript
I watched Anders Hejlsberg introducing typescript in 2012. Then I thought I don't have the problem it tries to solve. Type in Javascript has not yet caused any problem for me, as I have unit test, renaming and intellisense is also not a problem for me, because I have webstorm. Then, I found that Douglas Crockford said something in google plus
Microsoft's TypeScript may be the best of the many JavaScript front ends. It seems to generate the most attractive code. And I think it should take pressure off of the ECMAScript Standard for new features like type declarations and classes. Anders has shown that these can be provided nicely by a preprocessor, so there is no need to change the underlying language.
I think that JavaScript's loose typing is one of its best features and that type checking is way overrated. TypeScript adds sweetness, but at a price. It is not a price I am willing to pay.
I was kind of thinking the same way. So I didn't use Typescript in my all my projects until 4 months ago, when I joined a angularjs project where typescript is designated to be used. Now I feel that, type can be just a ceremony if it is not used properly, but it can be good enhancement of Javascript if used in the right way. In the following, I will list some of bad and good example of using typescript. The good examples can reduce the cost of using type without losing its benefit.
Don't annotate type for variable with value of built-in type, use inference.
//bad var n: number = 7; var s: string = 's'; var re: RegExp = /\s+/; var itemsOfString: string[] = ['one', 'two', 'three']; var callbacks: ((input: string) => number)[] = [ function (a: string) { return Number(a); }, function (b: string) { return Number(b); }]; //good, typescript can inference the type from value var n = 7; var s = 's'; var re = /\s+/; var itemsOfString = ['one', 'two', 'three']; var callbacks = [ function (a: string) { return Number(a); }, function (b: string) { return Number(b); }];
Use type annotation sparingly for custom type.
interface IPerson { firstName: string, lastName: string } function printPerson(person:IPerson) { console.log(person.firstName + ',' + person.lastName); } //bad, this annotation is unnecessary printPerson(<IPerson>{ firstName: 'John', lastName: 'Doe' }); //good, because the annotation can give you intellisense var john: IPerson = { firstName: 'John', lastName: 'Doe' }; printPerson(john); //good, intellisense is available from inference printPerson({ firstName: 'John', lastName: 'Doe' });
Don't create unnecessary interface or class if it is one-off thing, use inference
//bad, you need to duplicate type information from the object interface Customer { firstName: string, lastName: string } function createCustomer():Customer { return { firstName: 'John', lastName: 'Doe' }; } //good, typescript compiler can infer the return type function createCustomer(){ return { firstName: 'John', lastName: 'Doe' }; }
Don't create interface if "named anonymous type" is available
//bad, you need to duplicate the type information from the object namespace demo { export interface Settings { svcUrl: string } angular.module("demo").value("settings", { svcUrl: 'http://foo.com' }); } //good namespace demo { var settings = { svcUrl: 'http://foo.com' }; export type Settings = typeof settings; angular.module("demo").value("settings", settings); } // namespace demo { //use settings with type annotation angular.module('demo').controller('main', function (settings: Settings) { console.log(settings.svcUrl); }); }
Don't create class if all you need is an interface, because typescript is using duck typing
//bad class Customer { firstName: string; lastName: string ; } //good interface Customer { firstName: string; lastName: string ; } function logCustomer(cust: Customer) { console.log(cust.firstName + ', ' + cust.lastName); }
Don't use multiple assignment to define an object, use object literal expression
//bad, because typescript cannot infer the type of return object function createCustomer () { var rtn : any = {}; rtn.firstName = 'John'; rtn.lastName = 'Doe'; return rtn; } //good, because typescript can infer the literal object function createCustomer () { return { firstName: 'John', lastName: 'Doe' } }
Don't use module if it is internal module, use namespace
//bad module demo { } //good namespace demo { }
Don't use complicated construct, if simple construct is good enough
namespace demo { //bad export class Data { public static get key1(): string { return "value1"; } public static get key2(): string { return "value2"; } } //good export var data = { key1: 'value1', key2: 'value2', } }