50 ms·
Learn how to unleash the full potential of the type system of TypeScript
- downvoteme77221 4y agomy HTML blog only has 30 lines of JS. i didn't need a framework and i certainly don't need types. grumble grumble web should just be simple html javascript grumble serverside render in my pet language grumble /s
- jaredcwhite 4y agoI feel seen.
- cutler 4y agoAdd another 1 to your army. We all know how Typelevel Scala ended.
- aussiesnack 4y agoLooks good. Given the course is unfinished, it's a shame the only way to get updates is via a third party corporate web site ("Twitter"). RSS would be the obvious way to go.
- remram 4y agoTwitter seems like a good way to miss it. I don't check Twitter every day...
- aussiesnack 4y agoI don't have (or want) a Twitter account, which kind of rules me out.
- remram 4y agoI was agreeing with you, and adding that even if you have a Twitter account, you would still be likely to miss an update to the tutorial among the author's other Twitter posts. It seems that the author added a way to get notified by email, which might work for you: https://type-level-typescript.com/03-objects-and-records https://type-level-typescript.com/03-objects-and-records Personally I set a crontab to alert me if the next chapter page's size increases above 50kB.
- aussiesnack 4y agoThanks, hadn't spotted the email addition.
- d4mi3n 4y agoI'm always impressed at how much the type system in TS is capable of. The provided examples remind me of what I'd expect in something like Rust; it brings me joy that we can do this sort of stuff in our frontend code and tooling these days. We have come far.
- danellis 4y agoI'm currently working on a project that uses both Typescript and Scala. The overlap in concepts is rather useful when switching contexts.
- YoannMoinet 4y agoI particularly love the interactivity of the training. So polished. Can't get enough of the fireworks!
- bwestergard 4y agoThis is fantastic! I've often had to advise coworkers to read documentation for OCaml or Rust to learn idiomatic, functional, statically typed programming. It's great to see a Typescript specific resource with exercises.
- simlevesque 4y agoWow I think I know a lot of Typescript but I'll have to go through it because I'm always asked for ressources to get started and this one seem great. I also recommend type-challenges: https://github.com/type-challenges/type-challenges https://github.com/type-challenges/type-challenges It works great with the VSCode extension.
- krembanan 4y ago> It works great with the VSCode extension. What do you mean? Which extension?
- simlevesque 4y agohttps://marketplace.visualstudio.com/items?itemName=YRM.type-challenges https://marketplace.visualstudio.com/items?itemName=YRM.type...
- mikessoft_gmail 4y ago
- nonethewiser 4y agoThis is a really nice website.
- guggleet 4y agoSeems like a good start, but there are a lot of interesting offerings in the introduction that really don't exist in the content so far. Maybe finishing one of the more advanced chapters would be enough to lure people who are more experienced to check back on progress / pay / whatever you want traffic for.
- Myrmornis 4y agoThis looks fantastic, not just for people learning Typescript, but I'd think it would be useful (when completed) as an introduction to generics and type-level thinking etc for lots of newcomers to those areas.
- arberx 4y agoThere are only 3 chapters so far...
- Ayc0 4y agoI love the content!!
- willthefirst 4y agoGreat content. Give it me to me as a daily-dose newsletter :)
- amadeuspagel 4y agoLooks cool. I like how it gives immidiate feedback, but doesn't feel constraining or arbitrary. Is there are more basic tutorial in a similar style?
- Rapzid 4y agoI wish this issue would get addressed: https://github.com/microsoft/vscode/issues/94679 https://github.com/microsoft/vscode/issues/94679 Showing fully resolved types in Intellisense would be the single largest usability enhancement they could make for me right now..
- uup 4y agoThe Id<T> type defined in the first comment seems like a pretty good, albeit ideally unnecessary, workaround.
- Rapzid 4y agoThose hacks work but in practice I wouldn't classify them as "good". You end up having to look at a ton of types including in library code like React and etc that can be quite complex. Having to stop and try to wrap those on the fly is terrible ergonomics.
- uup 4y agoYou might also be able to leverage typeof in conjunction with Id<T>. Like if you have some parameter x with a complex type you can create a temporary variable of type Id<typeof x> to avoid looking up any additional types. Totally agree it should be supported out of the box, however.
- lf-non 4y agoYeah, this is a recurring pain. I know the universe at large has moved away from eclipse, but I loved their rich tooltips where you had nice structured representation (not just a blob of text from lsp) and could click through and navigate the type hierarchy.
- 9dev 4y agoTry any IntelliJ IDE, they’ve got that. I can’t understand why anyone would want to use VS Code with a Typescript project voluntarily…
- agentultra 4y agoNice course, and nice site. Although, as a Haskell developer, I am curious what type system TS is using (System F? Intuitionist? etc) and what limitations one can expect. Aside from the syntax of TS being what it is, what are the trade-offs and limitations? I was under the impression, and this was years ago -- things are probably different now?, that TS's type system wasn't sound (in the mathematical logic sense).
- AaronFriel 4y agoI don't believe it is using any type system described outside of the TypeScript compiler. The goal of the system is to accurately describe real-world JavaScript programs and semantics, not to introduce a formalization that existed elsewhere and impose it on JS. As a consequence, it has aspects of structural types, dependent types, type narrowing, and myriad other features that exist solely to model real-world JavaScript. As far as soundness: it's not a goal of the type system. https://www.typescriptlang.org/docs/handbook/type-compatibility.html#a-note-on-soundness https://www.typescriptlang.org/docs/handbook/type-compatibil...
- 323 4y agoNon-soundness is sort of a feature, it lets you force your way through and just say "trust me, this is a Thing" when it's just hard (or impossible) to make TypeScript see that. In practice, you can write large code bases where you only need to do this every 1000 lines or so. Not ideal, but better than no typing.
- agentultra 4y agoIs it fair to say that the limitation is that the type checker can admit programs that are not type correct?
- 323 4y agoYes. For example it has the "any" type which will bypass any sort of type check. But I think it's more nuanced. Depends of what you mean by type correct. Even in Haskell you can override the compiler and say "trust me on this".
- kaladin_1 4y agoJust came here to say that this is really nice. Fantastic way to play with Typescript Types even for people with decent knowledge of Typescript as I would consider myself. Particularly enjoy the confetti :)
- cercatrova 4y agoSpeaking of types, what are your thoughts on fp-ts if you've used it? It brings functional programming concepts like in Haskell such as monads into TypeScript.
- seer 4y agohaven’t used it myself but other teams at the company I work for have tried with mixed results. It’s very opinionated about the way you structure your code and basically makes anything thats not fully fp-ts hard to integrate, and also is quite hard for general JS people to wrap their head around. It’s been designed by FP people for FP people and if there are some on your team who are not fully on board or are just starting to learn FP - expect lots of friction. At my company it was mostly scala coders and “cats” lovers (category theory stuff lib for scala) mixed in with regular nodejs devs and I could sense a lot of animosity around fp-ts and its use. But on a more practical note, the more they converted their codebase to fp-ts the more they reported massive compile time slowness. Like it would start to take minutes to compile their relatively isolated and straight forward services. From what I gathered, if you want to go fp-ts its just too much friction and you’re much better off picking up a language designed from the bottom up for that - scala / ocaml / elixr / etc. To be honest once I’ve been comfortable enough with the more advanced TS features, you can write plain old javascript in a very functional style, and thats actually pretty great, especially if you throw date-fns, lodash/fp or ramda into the mix, and it remains largely approachable to people outside of FP and you can easily integrate external libs.
- cercatrova 4y agoThat sounds about what I've expected. Frankly in a TS codebase with many other devs that are not versed in FP, I wouldn't want to bring in a pure FP library because it, like you said, needs everyone to understand the "meta-language" of FP so to speak, such as how monads work, not having raw side effects, mutation etc. Ramda et al seem like a good compromise. Looking through its docs though, doesn't JS have a lot of this stuff covered? ie filter, map, reduce etc. What new stuff is it bringing in that covers say the 90% of most use cases?
- AkshatJ27 4y agoThis reminded me of the Typescript type-level Quine. https://youtu.be/UzE6d1ueT_E https://youtu.be/UzE6d1ueT_E
- cogman10 4y agoA great idea. Now, everyone that learns this stuff, show some restraint! The drawback of a powerful type system is you can very easily get yourself into a type complexity mudhole. Nothing worse than trying to call a method where a simple `Foo` object would do but instead you've defined 60 character definition of `Foo` capabilities in the type system in the method signature. Less is more.
- marcosdumay 4y agoThe types in your code are just as designed just like any other aspect of it. It's not a matter of restraint, it's a matter of doing things on the correct way.
- dllthomas 4y agoSo less can be more but more can also be more, more or less?
- marcosdumay 4y agoExactly. "More" and "less" are pretty much wrong ways to measure it.
- cogman10 4y agoI don't believe in "correct" when it comes to software dev. There's 1000 solutions to any given problem. It's a matter of choosing a solution that is clear, easy to understand, and easy to maintain. There are nearly limitless solutions that can fit that definition. Restraint comes into play because devs tend to "treat every problem like a nail when they have a hammer". When devs learn new concepts, they often look for places to use that concept even when it's a bad fit. An example of this is excessive use of inheritance when simpler types fit better. Many of us have dealt with the greenhorn that creates a giant inheritance tree or generic mess after they first learn that "neat" concept.
- deleted 4y ago[deleted]
- HellsMaddy 4y agoThis is great. Can you please create an email list where we can sign up to be notified when new chapters are available? If I follow you on Twitter, I will invariably miss any announcements.
- classified 4y agoIs TypeScript's type system Turing-complete?
- 9dev 4y agoWhy yes it is: https://github.com/microsoft/TypeScript/issues/14833 https://github.com/microsoft/TypeScript/issues/14833
- 323 4y agoOne think I struggled a lot with until I got it is that the TypeScript types and the JavaScript code live in totally separate universes, and you cannot cross from the type world to JavaScript values because the types are erased when transpilling - meaning they can't leave any trace. This means that it's impossible to write this function: function isStringType<T>(): boolean { return ... } const IS_STRING: boolean = isStringType<string>(); At best you can do something like this, which is inconvenient for more complex cases: function isStringType<T, IsString extends boolean = T extends string ? true : false>(isString: IsString): boolean { return isString } const IS_STRING_1: boolean = isStringType<string>(true); // compiles const IS_STRING_2: boolean = isStringType<string>(false); // type error You basically need to pass the actual result that you want in and just get a type error if you pass in the wrong one. Still better than nothing. Link if you want to play with it online: https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABDAzgZSgJxmA5gFQE8AHAUwB58AaRASXSx10VIA8pSwATFRAIzhwANqQCGSALyJ8Ldpx6IUjPIgD8iLCFKIAXImCihKUgD4AFKgzY8e+laYBKPQOFikAb0SZSUEJiSWyswAvgBQoRAISnRoAPpo+ABKtAByAOKxAIzOgiLiiFKB1gQkFErF5pqkDgDciAD09YiRALbEMCIoEVFQMfFJqRkATDmu+YUMxURk5OVM5gZG1XWNGqUsmJhwmKFAA https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABDA... Put another way, you can't do reflection with TypeScript. You can write that function in C++ templates, and I naively assumed that it's possible in TypeScript too, since from my observations TypeScript allows complex typing to be expressed easier in general than C++.
- rideg 4y agoYou can use type guards for something similar: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types https://www.typescriptlang.org/docs/handbook/advanced-types....
- kbr- 4y agoIs there any place I could report issues or ask questions about the course other than Twitter? If the author is reading this, the proposed solution to the `merge` challenge is: function merge<A, B>(a: A, b: B): A & B { return { ...a, ...b }; } That's the "obvious" solution, but it means that the following type-checks: const a: number = 1; const b: number = 2; const c: number = merge(a, b); That's not good. It shouldn't type check because the following: const d: number = { ...a, ...b }; does not type check. And I don't know how to express the correct solution (i.e. where we actually assert that A and B are object types). Also, looking forward to further chapters.
- KrishnaShripad 4y ago> And I don't know how to express the correct solution (i.e. where we actually assert that A and B are object types). You can do this: function merge< A extends Record<string, unknown>, B extends Record<string, unknown> >(a: A, b: B): A & B { return { ...a, ...b } } const result = merge({ a: 1 }, { b: 2 })
- ttymck 4y agoWhy Record and not object?
- lediur 4y agoLinters for TypeScript recommend using `Record<string, any>` instead of `object`, since using the `object` type is misleading and can make it harder to use as intended. See: - https://typescript-eslint.io/rules/ban-types/ https://typescript-eslint.io/rules/ban-types/ - https://github.com/typescript-eslint/typescript-eslint/issues/2063 https://github.com/typescript-eslint/typescript-eslint/issue... - https://github.com/microsoft/TypeScript/issues/21732 https://github.com/microsoft/TypeScript/issues/21732 - https://github.com/microsoft/TypeScript/pull/50666 https://github.com/microsoft/TypeScript/pull/50666
- davidatbu 4y agoAvoid the Object and {} types, as they mean 'any non-nullish value'. This is a point of confusion for many developers, who think it means 'any object type'. https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/ban-types.md#options https://github.com/typescript-eslint/typescript-eslint/blob/...
- gitowiec 4y agoHi n I have a question. I will go as simple and short as possible. I joined a small team working on the internal invoicing tool. Backend is Spring. Front-end is ExtJS used for me in very peculiar way. It emulates Java classes, there are Ext.define declarations with FQN names eg: "com.projectName.ds.Board.ui.extjs" (as string, casing important) Then in the code this class is instantiated by its FQN but used as identifier eg: var Board = new com.projectName.ds.Board.ui.extjs(); There are also a lot of FQNs with short namespaces, different are associated with business short names and other like Dc, Ds, Frame belong to code architecture domain (data controller, data store, a frame on the screen). How I could use typescript to improve developer experience here? I'm from the react world, I programmed 4 years only in typescript, react, node and mongo. Thanks!
- theteapot 4y agoDidn't know `Expect` existed. Can't find it docs?
- gvergnaud 4y agoIt's actually not in the standard library, but you can write it yourself: type Expect<T extends true> = T; `T extends true` puts a type constraint on the parameter, which then needs to be assignable to the literal type `true` to type-check.
- theteapot 4y agoOK cool, now what about `Equal` :).
- triyambakam 4y agotype Equal<A, B> = A extends B ? (B extends A ? true : false) : false;
- theteapot 4y agoNice.
- gvergnaud 4y agoActually, this won't work with union types! The definition of `Equal` I use is this one: type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends < T >() => T extends Y ? 1 : 2 ? true : false; Understanding this requires a bit more context, but I'll explain why we need something so complicated in the Advanced Union Types chapter :) I picked it from https://github.com/type-challenges/type-challenges https://github.com/type-challenges/type-challenges which is an awesome resource too
- tunesmith 4y agoMight as well ask here. On our teams, we have the occasional developer that is insistent on using Typescript in an OO fashion. This has always struck me as square peg round hole. Even though I come from an OO background, Typescript strict settings really seem to push me in a direction of using interfaces and types for type signatures, and almost never classes, subclasses, instantiated objects. I don't have a very good answer for "yeah, but what about dependency injection"? though. Any thoughts from anyone?
- WHATDOESIT 4y agoAn ES module is encapsulated enough. You can dependency inject with them as you wish. It's like having a class, no need for a class inside a class. The biggest argument is that my functional-ish code is always 3x shorter with the same features, though.
- bilalq 4y agoThis. Creating classes to wrap dependencies is a pattern only needed because of language limitations. With JS/TS, you can mock at the import statement level, so no need to twist your code to abstract away importing. Also, even if you didn't want to mock that way, you can get dependency injection with functions just by taking a parameter for a dependency. If dependency injection is the only reason you have to use a class, you probably shouldn't use a class.
- spion 4y agoRequest-scoped DI (as seen in ASP.NET MVC) is great on the backend for servicing requests. You can ask for e.g. a class representing the current user information to be injected anywhere, or to keep track of request-associated state like opentelemetry spans, or a transaction, etc. The alternative is to pass the user information class or transaction to all other services, which can be annoying Its rarely seen in the ecosystem as a solution, unfortunately (everyone is passing all arguments all the time), but its one of the rare places where this is still useful. I've had bad experience with the alternative (continuation local storage) and its not nearly as elegant.
- theteapot 4y agoCourse is probably great, but I find it really weird and unnecessary to describe Typescript type system as Turing complete. Who cares?
- lolinder 4y agoPeople who are trying to statically describe the behavior of highly dynamic JavaScript code.
- theteapot 4y agoYeah but how is Turing completeness directly relevant to that? Article doesn't seem to explain, just says "Turing Complete" in the title. Again, so what? I suppose it's somewhat indirectly vaguely reassuring? P.S. You might like this http://beza1e1.tuxen.de/articles/accidentally_turing_complete.html http://beza1e1.tuxen.de/articles/accidentally_turing_complet...
- deleted 4y ago[deleted]
- somewhereoutth 4y ago> Over the years, the type system of TypeScript has grown from basic type annotations to a large and complex programming language. Give someone (particularly a developer) the opportunity to build something complicated and undoubtedly they will. So now you have two problems, the complicated program that actually does some hopefully useful work, and another complicated program on top of it that fills your head and slows you down when trying to fix the first complicated program. You may say 'ah yes, but the second complicated program validates the first!'. Not really, it just makes things more complicated. Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs. However, static analysis of existing code (in Javascript), without having to write a single extra character, may well have great value in indicating correctness. Edit: > TypeScript's type system is a full-fledged programming language in itself! Run! Run as fast as you can! Note that this 'full-fledged programming language' doesn't actually do anything (to their credit they admit this later on) Edit2: > [...] is a type-level unit test. It won't type-check until you find the correct solution. > I sometimes use @ts-expect-error comments when I want to check that an invalid input is rejected by the type-checker. @ts-expect-error only type-checks if the next line does not! What new level of hell are we exploring now?? I am genuinely afraid and I'm only halfway through this thing. What's next? A meta type level language to check that our type checking checks?? > 3. Objects & Records > COMING SOON! > this chapter hasn't been published yet. Thank God, I am saved.
- edgyquant 4y agoHave you worked with typescript? Adding strict types to JavaScript fixes pretty much all the complaints I use to have when working with frontend services/clients. Typescript isn’t a “now you have two problems” anymore than types in any other language are.
- somewhereoutth 4y agoMaybe in a controlled environment Typescript can produce benefits - however much of my argument is that our environments are typical uncontrolled, let's not give the monsters any more magic than we have to.
- robertwt7 4y agoReally interesting articles, will definitely have a look at night. After so many years of JS programming, moving to a company that uses TS extensively (in a huge scale) feels life changing. You don't even know the effect until you use it daily. Even so, daily usage of typescript at a large web application doesn't seem to be using its full potential. I feel like libraries creator and maintainer use them more in the definitions that they created (i.e redux toolkit type is mind blowing). Thanks for creating this lesson, it will definitely teach me a lot
- Benjamin_Dobell 4y agoOne of the worst things about Next.js, Remix etc. is their file system driven routes. I really wish these frameworks would stop relying so much on hidden magic. Conventions are good, but as to why those conventions aren't in code is quite peculiar. Previously, I wrote my route definitions with types for both path params and query params in one file, and used TypeScript to enforce that the equivalent back-end definitions (async loaders etc.) and front-end definitions (React components) were kept in sync. When I first implemented this in a previous project, I found many instances where routes were expecting query params but they were being dropped off (e.g. post login redirects). Supporting things like parameters for nested routes certainly means the TS types themselves are non-trivial, but they're the kind of thing you write (and document) once, but benefit from daily. Examples of stuff that can and should be 100% type checked: // ... template: { path: idPath("/template"), subroutes: { edit: { path: () => "/edit" }, remix: { path: () => "/remix" }, }, }, onboarding: { path: () => "/onboarding", query: (params: { referralCode?: string }) => params, subroutes: { createYourAvatar: { path: () => "/createyouravatar" }, }, }, // ... Routing: // Path params navigate(routes.template.edit({ id: props.masterEdit.masterEditId })); // No path params, just query params (null path params) navigate(routes.onboarding(null, { referralCode })) // Nested route with query params (inherited from parent route) navigate(routes.onboarding.createYourAvatar(null, { referralCode })) React hooks: // Path params const { id } = useRouteParams(routes.template.edit) // Query params const { referralCode } = useRouteQueryParams(routes.onboarding); API routes: // ... play: { path: () => "/play", subroutes: { episode: { path: idPath("/episode"), }, }, }, // ... Relative route paths (for defining nested express routers): const routes = relativeApiRoutes.api.account; router.post(routes.episode(), async (req: express.Request, res: express.Response) => {
- presentation 4y agoremix-routes and next-type-safe-routes both allow for tacking on type safety here with a build step, not ideal but 90% of the benefit from that.
- Benjamin_Dobell 4y ago
- nsxwolf 4y agoI currently have to occasionally contribute to a TypeScript codebase at work. I appreciate how much better it is than Javascript. When I write code as an outsider (Java and Go developer), I feel like I use the type system in sensible and readable ways. When I look at the code written by the native TypeScript experts in my company it is a bewildering, abstract, unreadable morass. I have no idea what's going on half the time.
- solardev 4y agoYeah, what a terrible syntax :( Just today I was looking at the type definition for a third-party lib (ramda)... what the heck does this even mean... compose<V0, V1, V2, T1, T2, T3, T4, T5, T6>(fn5: (x: T5) => T6, fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T6; Got it?
- 29athrowaway 4y agoIt's very simple to understand. You don't have to actually read the definition just know what "compose" does at a high level.
- solardev 4y agoWhat compose() does is not the point here, it's that the type definition is totally unreadable. I was trying to figure out what compose was supposed to return (the function or the value), in that case, and I still don't really know. Another random example from Axios: <T = V>(onFulfilled?: (value: V) => T | Promise<T>, onRejected?: (error: any) => any): number; Or eslint: type Prepend<Tuple extends any[], Addend> = ((_: Addend, ..._1: Tuple) => any) extends (..._: infer Result) => any ? Result : never; Here's another real example from today... I was trying to figure out how to type "the name of this type's key has to be one of the following strings in this enum, but the type doesn't need to have all the keys". Here's a Stack link with the right answer: https://stackoverflow.com/a/59213781 https://stackoverflow.com/a/59213781, but it wasn't easy to figure out. At first I thought it would be `[key in Partial<MyEnum>]`, but nope. Maybe optional? `[key in MyEnum]?` kind of works but fails in an new way (see the Stack for details). The correct way to do it is apparently `Partial<Record<MyEnum, unknown>>`, which I NEVER would have been able to figure out. Why the record? Why the unknown? Who knows..? Don't get me wrong, I love TypeScript for the simpler use cases, and a lot of it IS that, thankfully. But the more complex compositions, especially in popular third-party libs? I've given up lol. The use of single-letter keywords (K, T, V, P, R, etc.) combined with confusing re-use of punctuation (<> and : and () and []) that mean subtly different things depending on where they're used, on top of how JS already uses them, makes it even more so. Sometimes I wish TypeScript were more verbose and opted for longer, clearer constructs rather than stacked shorthands...
- jheriko 4y agoreminds me of C++ templated types being used for similar... except in this case there is no performance advantage from removing run-time logic by force. this kind of stuff is often confusing when working with teams. using simple dumb stuff is always the better option when you can.
- vladde 4y agoSimilar to the paid tool https://www.executeprogram.com/ https://www.executeprogram.com/ , which goes very much more in-depth with TypeScript's type system (as well with other languages)
- SeriousM 4y agoTypes are here to help but with this power comes great responsibility. The deeper you allow an input to be used in your library/method the more it makes sense to put a well defined type contract on it. Exhaustive type definitions may show that the author doesn't have a understanding of a required interface, abstractions or pattern to use. I use c# professionally for 20 years and I love the type system. It helps to tell the contract about the types and functions you expect yet you can overcomplicate things if you really about to. c# makes it "hard" to create method type definitions and you should use interfaces to achieve the contract definition. This helps to avoid the inline type definition as it's done in typescript. My approach is to use types in typescript as I'm used to in c#.
- jawadch93 4y ago
- throw10920 4y ago> Expect<Equal<Hello, "World">> This seems like a great time to bring up "Why Static Languages Suffer From Complexity"[1], which explains the "statics-dynamics biformity" that leads to languages like TypeScript that are actually two languages: the runtime one and the compile-time type-system one. [1] https://hirrolot.github.io/posts/why-static-languages-suffer-from-complexity https://hirrolot.github.io/posts/why-static-languages-suffer...