- The equality operator (“==”) type coercion rules are confusing and error-prone
- “This” keyword can refer to a different object when used inside a function depending upon the way that function was invoked
This overview focuses on TypeScript and tries to evaluate what it can offer to help with client-side application development on the web.
This description embodies two of the first TypeScript design goals, which are:
- To statically identify constructs that are likely to be errors
- To provide a structuring mechanism for larger pieces of code
- Making it easier to write correct and predictable code, eliminating common mistakes like typos or incorrect assumptions about a type of value. These mistakes are caught at compile time by a type checker, as opposed to dynamic languages, which typically require writing more unit tests to cover these cases. That does not mean that an application written in TypeScript does not require unit tests, though. It just means that it needs fewer of them.
- With the knowledge about types an IDE can be much more helpful, providing features like autocomplete. This makes APIs more discoverable, freeing a developer from the necessity of checking with the API reference all the time or knowing all of the API details by heart. This often brings a significant productivity boost.
- It makes it possible to perform automatic and safe code refactoring like renaming a function or a variable. Such tooling support, along with the ability to navigate the code more easily (with features like “go to definition” and “find all references”) makes it easier for developers to work with a large codebase and keep it in a maintainable state.
When it comes to structuring mechanisms, TypeScript introduces the notion of class-based object-oriented programming. This makes it fairly simple to structure code into classes, create class hierarchies, and manage the scope and visibility of methods and data throughout the system.
TypeScript also uses the concept of modules, internal and external.
TypeScript’s internal modules are similar to namespaces (like the ones available in C#) and allow defining symbols (variables, functions, classes) off of the global scope. Internal module declaration can be split into multiple files. This encourages a development team to have a well structured project with small single-purpose files.
Here are the strong points of TypeScript:
- Rich IDE support with autocomplete and code navigation features
- Safe automatic refactorings
- Discoverable APIs and more self-explanatory code contracts (through type declarations)
- Typos caught at compilation time
- Class-based OO, with inheritance, private members and interfaces
- Module support
- Relatively low risk of trying TypeScript out:
Those benefits are a great help when dealing with a mid- to large-sized codebase and a team of developers working on a project. TypeScript brings the development experience closer to what Java and C# developers have come to expect from their respective platforms in terms of productivity and maintainability.
- Quality of ambient module definitions (“.d.ts” files for third-party libraries) sometimes leave much to be desired. Certain definitions might be too specific or not specific enough, making you jump through hoops trying to make the type checker happy (adding some type-casing statements or worse, casting type to “any” which basically defeats the purpose of TypeScript’s strong typing in those instances).
- Not all third-party libraries have ambient definitions available. The development team will have to write and maintain their own if they want to use a particular library and take advantage of TypeScript.
Here are some tips that can make TypeScript programming easier:
- Do use type declarations. The effort required to avoid using “any” will pay off at the time of refactoring. TypeScript handbook is a good source of information. Here are some useful sections:
- Be prepared to write or adapt existing ambient module declarations.Following links provides guidance and best practices:
- Avoid common pitfalls connected with modules:
- Use the “export = ClassName” construct when declaring an external module with a single class or function. It’ll avoid needless namespacing in the code that uses the module.
- Remember that paths to external modules are case-sensitive.
- Use “amd-dependency” tag (using “/// <amd-dependency path=’…’ />” syntax) instead of “import” keyword to declare a dependency on a non-code module when using RequireJS plugins like “text!”. The “Loader plugins” section of this article provides more details on that topic.
- Consider having a single “references.ts” file with all the references to internal modules and ambient declarations. Add a reference to it at the beginning of each TypeScript file (using “/// <reference path=’…’ />” syntax). Reference tags are always relative to the file they are declared in, so this approach can make it easier to write and reorganize files in a project, compared to a situation when each file references all ambient and internal modules it uses directly.
- Adopt coding guidelines. This is the one used for TypeScript compiler development.