The type system of TypeScript goes a lot further than most mainstream languages namely in structural typing, value types, type disjunctions and flow based typing. I’ll give a quick cute example to demonstrate the power of its type system using the builder pattern. In this pattern methods return this
so you can chain assignments. It’s a pattern that is very difficult to extend.
Let’s say I have a catalog of books. So I create a Book
class. Then I start selling some books. So I extend it with a PricedBook
. Something like this in Java:
1public class Book {2 public Book setTitle(String title);3 public Book setAuthor(String author);4}56public class PricedBook extends Book {7 public PricedBook setPrice(String price);8}910new PricedBook()11 .setTitle("Donkey Time")12 .setAuthor("JL2352")13 .setPrice("10 dollars price")
The above is not valid. Why? Because setAuthor
and setTitle
return a Book
not a PricedBook
. I could move setPrice
up, but that’s annoying. There are ways of fixing the return type, but they are non-trivial.
In TypeScript if I change the return type to the value this
then it just works. For example:
1class Book {2 public setTitle(title: string): this;3 public setAuthor(author: string): this;4}56class PricedBook extends Book {7 public setPrice(price: string): this;8}910new PricedBook()11 .setTitle('Donkey Time')12 .setAuthor('JL2352')13 .setPrice('10 dollars price'); // this works fine
The compiler can tell that this
is a PricedBook
when that’s the object being chained. The code size is the same, yet it’s more expressive about the types involved, compiles, and it’s still type safe.
Now, let’s take a step back. What we have above is two bits of data which are similar, but not quite the same. Books
, and PricedBooks
. There are actually a fair few more tricks around how you can deal with this in a type-safe manner. One is optional fields, and another is something like:
1type StoreBook = Book | PricedBook;
With the new StoreBook
type, I can only use things which are present in both Book
and PricedBook
. So I can use title and author, but not price. Well, that’s type safe, but what if I do want to show the price? Well, the compiler will let me use price as long as I can prove it’s there. One way I can do this is if I prove it’s a PricedBook
(since it has a price):
1const book: StoreBook = getBook();23// Because I test the type, no cast is needed.4if (book instanceof PricedBook) {5 book.setPrice('20 dollars');6}
In Java, I’d need a cast to get the above to work. That’s reliant on the developer always putting their casts in the right places. In TypeScript as long as I can prove something, then the compiler will go along with it (although the proofs you can do are all trivial as the compiler has only so much intelligence).
The above examples are a little contrived but are based upon real problems I have run into. The point I’m making is that the type system is just a tad more intelligent. As a result, I can write code which is a little tighter and has less boilerplate, than if I used say Java. It’s still type safe.