I am quite surprised at the turn out in the comments here against adding type checking to JavaScript.
In my opinion, TypeScript is not only essential in the context of any professional project, but it features one of the most ergonomic type systems I have ever worked with.
There are certainly pain points with certain TypeScript features (e.g. enums) but any project that takes me longer than 5 minutes to write, I need type checking. If I can't be bothered with setting up tsc - and setup difficulty is a valid criticism - I just use jsdoc.
I have seen TypeScript take the heat when applied to JavaScript projects that implement multiple trendy programming paradigms. Often times the projects themselves are so complex that adding a type system requires type-kungfu. It's not the type system at fault - but a needlessly complex architecture.
Love TypeScript. Wish there was anything like it that compiled down to static binaries.
Having worked with OCaml and F#, I find that TypeScript requires fewer changes to non-type code in response to changes to the types.
For instance, given `type T = { a: string; b: string; c: string }`, if I want to make `b` and `c` nullable but always provided together:
// OCaml
type TOpt = { a: string; bc: (string * string) option }
// TypeScript
type TOpt = T | { a: string; b: undefined; c: undefined }
With the TypeScript approach, code which worked with T still works with TOpt (only requiring an `if(x.b)` guard). With the OCaml approach, any code that accessed b or c needs to be rewritten.
The same applies to cases where an object can be in several different modes, but some properties are present in all modes. For example, an AST node can be a literal, identifier, binary operation, etc. but it always has a source location and an inferred type. In OCaml this has to be represented by either separating a "common properties" type from a "kind of node" union type:
type node = { loc: location ; inferred_type: exprtype option ; kind: nodekind }
and nodekind = Lit of string | Id of string | Unary of op * node
Or by repeating the common properties in all union type constructors:
type node = Lit of location * exprtype option * string
| Id of location * exprtype option * string
| Unary of location * exprtype option * op * node
Both are tedious (although F# makes the second one slightly less tedious by allowing one to define computed properties on union types). By contrast, TypeScript allows you to have only one type:
type NodeCommon = { loc: location; inferred_type: exprtype|undefined }
type Lit = NodeCommon & { kind: "lit"; value: string }
type Id = NodeCommon & { kind: "id"; value: string }
type Unary = NodeCommon & { kind: "unary"; op: op; node: Node }
type Node = Lit | Id | Unary
In my opinion, TypeScript is not only essential in the context of any professional project, but it features one of the most ergonomic type systems I have ever worked with.
There are certainly pain points with certain TypeScript features (e.g. enums) but any project that takes me longer than 5 minutes to write, I need type checking. If I can't be bothered with setting up tsc - and setup difficulty is a valid criticism - I just use jsdoc.
I have seen TypeScript take the heat when applied to JavaScript projects that implement multiple trendy programming paradigms. Often times the projects themselves are so complex that adding a type system requires type-kungfu. It's not the type system at fault - but a needlessly complex architecture.
Love TypeScript. Wish there was anything like it that compiled down to static binaries.