It's a bit of both for me. I'd say 90% of it is from simply having types; 10% is from some of the cool things you can do with TS types that aren't common in most statically typed languages.
For me (coming from C++) being able to easily union and intersect types, and being able to specify certain values (not sure the right name for this). For example:
type ServerResponse = {success:true, response:JSON} | {success:false, error:string};
In C++ I would probably have to type success:boolean, and make response and error optionals, but the TS type is more expressive.
if success==true, then response must exist.
if success==false, then error must exist.
TS understands this type really well, and type narrowing makes it easy to work with.
// NOT LEGAL; error might not exist
console.log(sr.error);
// IS LEGAL; we test for success first, which narrows the type.
if(sr.success){
console.log(sr.response);
} else {
console.log(sr.error);
}
Too late to EDIT: but "literal types" was the word I am looking for.
I used "success:true" and "success:false" as part of the two types I was combining; it seems like "success:boolean" would server the same function, but it does not. The type I created has more information that that.
I'm a big fan of TS, but discriminated unions are available in a lot of languages and when not, there's usually some library adding it like boost::variant if the language has runtime types or templating/macros.
Some folks have built whole SQL databases and DSL compilers in the TS type system. These tend to be toy projects with disclaimers not to use them. But the type system being Turing complete[0] (for better or worse), pretty much whatever you can imagine. This project[1] is one I actually return to frequently for practical ideas.
- easy referencing the type of another struct's field. As in `function parseInput(input: SomeType['someField']) { }`
- it's ability to infer types in general
- parsing. given a route string like `/users/:userId` TypeScript can force you to always pass `{ userId: 1234 }` when actually building the route without having to declare the interface anywhere