like what?
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.
const sr: ServerResponse = {success:true, error:"moo"); // won't compile, doesn't match
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);
}No, thanks.
I should have been specific, but all of the errors I mention above are compile-time errors, not run-time.
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.
https://www.typescriptlang.org/docs/handbook/literal-types.h...
- 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
- conditional types
The optionality is a special trait that most "real" static languages don't get to enjoy (for better or worse). By being optional, I can simply ignore types when I'm hacking/prototyping something and type safety/soundness is the last thing I care about. Rust, for example, can feel really tedious when it makes me "show my work" and I'm just trying to scribble something up quickly. If that makes sense? I just make `any` a linting error so I can use it during my experimentation/prototyping but I cannot ship it.
Sounds pretty extreme..? What kind of bottlenecks would you be talking about that would prompt me to re-write my entire application in another language?
And TypeScript has a much more expressive type system compared to Go, Java, and frankly most languages. Java in particular still has pretty bad type reification (where ArrayList<Integer> and ArrayList<Float> are essentially the same and cannot be used to overload methods, for example).
I use a lot of the utility TypeScript functions (i.e. Partial<>, Record<>) and it catches all sorts of mistakes that might be made by myself in the future or someone not familiar with the codebase. For example, if there has to be a A->B mapping somewhere, I would type B so it would raise a type error if something was added in A but not B. Most type systems are not flexible enough to do anything remotely like that and instead you have to write a bunch of tests where you end up 20 lines of very basic test code for 1 actual line of code.
This:
type Foo = {
bar: string
// …
}
Can just as easily be addressed as: const parseFoo = parse.object({
bar: parse.string,
// …
})
type Foo = Foo<typeof parseFoo>
(Where parse methods here are type guards for their respective parsed return types, and can similarly be used to serialize runtime values as needed.)For that little bit of extra ceremony, you get to isolate runtime stuff to the boundaries you need to care about, and let the type checker deal with everything else (typically most everything internal).
TypeScript types are not available at runtime. As the name implies, runtime type information requires runtime support. Since TypeScript compiles to JavaScript, the only type information available is that provided by JavaScript.
I can't think of anything I can do in .NET (which has runtime types) that I can't do in TS with the help of a library or two.
Libraries like io-ts and zod embed the type information into JavaScript, so the information is available at runtime but only for wrapped types. If TypeScript provided runtime type information, such libraries wouldn't be necessary.
const parseStr = (value: unknown): assert value is string => {
if (typeof value === 'string') return value
throw new Error(…)
}
const str = parseStr(anythingYouCanThrowAtIt)
I can 100% guarantee you str is going to have the same static and runtime type once you’ve checked it, or parsed it from whatever type you’d accept as a string. TypeScript won’t do the parsing for you, because JavaScript doesn’t have clear semantics for runtime casting that anyone wants or would accept. But type guards are exactly the solution to that and incredibly composable. Once you accept that reality and embrace it, getting the static type out of your runtime parser is a single added line of static type code. And all of this is almost exactly equivalent to what languages with runtime casts do, but you have complete visibility into it because you determine how casts behave. There are whole libraries which do this for you so don’t worry about rolling your own unless you have very particular needs. But TypeScript definitely has the facility to align static and runtime types however you see fit. You just need to tell the type system what types the runtime conveys, same as every other aspect of the type system.It’s not a workaround. Every language with runtime types will have some logic devoted to this kind of casting/narrowing. TypeScript rightly doesn’t build it into the compiler because most of the time you don’t want types to have runtime behavior. For internal logic which can be validated statically, runtime types would be an unnecessary overhead. So it’s up to developers to determine where it should be applied. Type guards are explicitly an affordance for that, designed specifically to convey types with runtime casting/narrowing.
If you use good, composable primitives like zod or io-ts or any of several other implementations, your code will typically be almost identical to deriving runtime from types rather than the inverse.
0: https://itnext.io/parse-dont-validate-incoming-data-in-types...
1: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
My only issue with TypeScript is its strange design with regard to interface and types. Those are pretty fundamental concepts and they're absolutely similar. I don't understand this design and I think that only one thing should have left, but may be I'm wrong about it. Also I think that TypeScript documentation would benefit from more examples with hard concepts, I didn't fully understand its advanced generics concepts. But that's not a big thing and probably more on me.
I feel the same way. IIRC, earlier versions of Typescript were much more clearly influenced by .NET and other Microsoft idioms, and there were a few features that didn't seem to make much sense coming from the Javascript side of things. I also had a buddy of mine that's a Java developer be very confused about how they were supposed to work.
> Also I think that TypeScript documentation would benefit from more examples with hard concepts, I didn't fully understand its advanced generics concepts. But that's not a big thing and probably more on me.
Absolutely agree on this, although things are getting better. For awhile it seemed like the only documentation for more advanced features was the main website's blog posts and release notes.
- documentation
- aids tooling (eg improved static references in editor, static analysis by linter, etc)
- catches a whole class of problems before runtime (eg a typecheck-time substitute for certain runtime checks and their corresponding tests)
I often like to add:
- promotes better design
This puts TypeScript’s type system in a unique position, where it’s simultaneously expressive and complex in ways many others are not (because it’s designed to express extraordinarily dynamic idioms used in JS) and equally mundane and simple if you use it from the start (because all that complexity is a good forcing function for deciding whether you all of that dynamism it can express is of value for the thing you’re actually building).
In a more general sense, it strongly encourages developers to think about their interfaces explicitly where they might otherwise “rapidly prototype” a monstrous one.
The vast majority of the time, TypeScript will encourage simpler APIs that don’t need most of TypeScript’s “advanced” type system features. And in terms of productivity, that’s a benefit you only get if you’re starting from the proverbial Dynamic Wild West. So it might not be absolutely more productive than other statically typed languages, but still relatively an enormous productivity boost in its context.
In general, if a "best practice" of a language is to write less code that tends to translate to great productivity
As far as I can tell, TypeScript doesn't add any new features or behavior to JS other than static typing.
If you ask me, of course TS isn't as good as the real deal, but it's darn close and way better than vanilla JS or compilers for other languages that target JS.
- type system is way more flexible. You can create, merge, pick keys and manipulate types in all kinds of ways. You have better type inference, type conditionals and lots more
-there's an escape hatch. Sometimes you just want to quickly try something, or you're under time pressure and need to either break the types or do a bigger rewrite, or you just don't care about a certain variable's type. Then there's the any/unknown/plain js/@ts-expect-error escape hatches to do just that.
Yes, there are a couple of weird edge cases with arrow functions in generics, and it gets a bit more complicated with narrowing, but aside from that this never a problem I've run into. IMO the verbose error messages for complex types are a much bigger problem, which could be solved with better UIs for error messages (syntax highlighting and code folding).
Also after coding 5 days a week for a full year with it I really can't relate to the idea that it is somehow unpredictable. I think, like most things, you eventually get a "feel" for it and don't even have to think about it.
That's what I love most about it. It feels like it takes up no extra mental real estate for me. It's excellent at never "getting in the way" (when used properly)
https://www.typescriptlang.org/play#code/MYewdgziA2CmB00QHMA...
Practice safe interactions - say no to unprotected injections.
Who to blame is less important when you have an abandoned/orphaned child processes to take care of :/
No kink shaming, tho.
And I think it is the responsibility of the type system to at least catch if I'm passing too many arguments to a function.
I understand why they chose to do this, but I reach for better type systems any time I have the choice.
There are people ready to defend with their life the most obtuse, inane piece of technology, just because they have invested a non-negligible amount of time getting used to its quirks, that they become completely blind to its faults, and start to believe those very quirks are actually positive and to be cherished.
This is alludes to my only major complaint about typescript. IMO, something is either type-safe or it ain't. I don't like how the JS bleeds through if you aren't careful.
I'm not a TS hater by any means, I use it regularly and am usually pleased. I just can't help but compare it to the [S,Oca]ML compilers I'm so fond of.
It took a long time for js devs to accept the notion of a compiler with TS. While TS is an improvement, it’s only a localized one. No chance ever, any other language could have replaced it.
There are perhaps at least a hundred (when not hundreds of) compile-to-js languages, yet people still use it. Even in back-end!
> It took a long time for js devs to accept the notion of a compiler
People who write programs in JS cannot be so easily grouped together, I think. There's this eternal September of bootcamp devs writing... interesting... code as we all did when we first started, but I personally went first with C, Perl, then PHP, then JS, then .NET, then Python and now continue mostly in TS and Java. Among them TS is the one I like the most (and the ones starting with P are the ones I dislike the most - not that it matters, use whatever works for you).