Types can be asserted at runtime (parsed) at IO boundaries (reading http request or response, websocket message, parsing json file etc). Once they enter statically type system they don't need to be asserted again.<p>The difference it makes is illusion of type-safety vs type-safety this article touches on.<p>It's basically mapping of an `unknown` type into known one, at runtime.<p>You can try to bind service with client (types) somehow but in many cases this will fail in production as you can't guarantee paired versioning, due to normal situations by design of your architecture or temporary mid-deployment state or other team doing something they were not suppose to do (new client is deployed connecting to old service or vice versa) etc. It's hard to avoid runtime parsing/type assertion in general.<p>Functional combinator approaches like [0] or faster [1] with predicate/assert semantics work very well with typescript, which is very pleasant language to work with.<p>[0] <a href="https://github.com/appliedblockchain/assert-combinators" rel="nofollow">https://github.com/appliedblockchain/assert-combinators</a><p>[1] <a href="https://github.com/preludejs/refute" rel="nofollow">https://github.com/preludejs/refute</a>
Very interesting topic, actually it's one of my favourite ones.
I've been working for the last year or so on the problem to improve type-safety for NextJS. My solution works like this:<p>Step 1: introspect your DataSources (Postgres, MySQL, GraphQL, REST, etc...)
Step 2: combine them into a virtual Graph (virtual GraphQL API)
Step 3: write GraphQL Operations and "compile" them into JSON RPC + generate a 100% type-safe client<p>Result? No more double declaration of types. No manual typing. More info on how this works with NextJS and Typescript here: [0]<p>Note, I'm the founder of WunderGraph and obsessed with creating the perfect developer experience for working with APIs. I appreciate any feedback.<p>[0]: <a href="https://wundergraph.com/docs/overview/features/generated_clients" rel="nofollow">https://wundergraph.com/docs/overview/features/generated_cli...</a>
Hey y’all! I’ve been a big nerd about all things React, Next and Typescript for awhile and wanted to formalize some of my thoughts and frustrations into this article.<p>I was lucky enough to get some awesome eyes on it early, including a handful of people from Vercel. I hope this sparks some good discussion!
That's nothing, there's also implicit boundaries across which NextJS will convert your objects to JSON, and NOT CONVERT THEM BACK AFTER, while pretending there's no change. Have a date in your object? Not any more pal, it's a string. You return an instance with methods in it? Wanna bet? How about a Map or Set? Guess what? Nah.
My biggest gripe with inferred types is that you wind up with errors at random usage sites instead of at where the actual type “error” is. For example, with inferred return types you can wind up returning `a | b` when you thought you were returning `a` and this might actually be coincidentally fine for a bit until you use it somewhere that only takes `a` and you get a type mismatch. You can then trace backwards until you find the source of the bad assignment, which can be a few layers after a bit of inference, and this always felt antithetical to the improved ergonomics of types. As a result I always enforce explicit return type annotations with inference only within scopes. But TS happily supports a spectrum!
It will happen. Eventually runtime / io typesafety will happen, just not yet. A good example of how this was solved in the past was Microsofts IUnknown interface where the type of a blob is determined after it is downloaded. It basically converts unknown to a type on access, but is just too heavy duty for bandwidth requirements in a runtime/JIT environment.
> with an implicit type contract (<i>potentially generated</i>) through the creation of these files<p>Racket is able to automatically convert static types of Typed Racket into contracts when values flow between typed and untyped worlds. This happens automatically and transparently, which means you don't have to worry about almost at all. One advantage Racket has over JS is the module system (well, it holds the same advantage over almost all the other languages), which allows typed and untyped code to reside in the the same file, yet have a clear boundary between them.<p>I can't find it right now, but there was a paper describing how it works. It's probably somewhere here: <a href="https://github.com/samth/gradual-typing-bib" rel="nofollow">https://github.com/samth/gradual-typing-bib</a> (if you're curious enough to read many tens of abstracts...)
As someone who writes fp-ts regularly: TypeScript has way too many ways to be type unsafe. Discipline and parsing API boundaries can give you typings you can 99% trust.<p>Discipline is very needed because of many factors, last but not least the fact that there is an abundance of type unsafe apis (e.g. JSON.parse).<p>The worse part isn't even that you cannot have total type safety imho, there has to be compromise for a language willing to be a JS superset. It's how difficult it is the type system when more advanced patterns are required, often the things are completely impossible.<p>Still, I would never do front end on a different language, and I do love elm and other type safe alternatives.
Don’t you need some pretty solid source data for you to trust them?<p>I only recently picked up typescript, coming from decades of .Net and later a Python, and the way you can turn things like json results into typed data via interfaces while also having the vast open source libraries I’ve done to love from Python, I gotta say it’s just been so easy to work in an enterprise environment where the source data is often really terrible.<p>I can certainly follow the points of this article. It is sort of silly to type an uuid as a string, but when does it actually break?
The one thing I would ask this person is how big and complex a system they've built like this.<p>Because if you optimize all the slack out of a system, you have no room to manoeuvre. In this case, you need to update all your code and dbs all at once if any type changes. Because it's all linked.<p>In my experience this is not feasible one you reach a certain size. You need to be able to upgrade parts in isolation while keeping the majority working without touching it.
What is the value proposition of all this for simple apis? The author is using one of the simplest examples, and almost all of the proposed solutions other than the first problematic one read like obtuse technical literature to me. All for what, so I don’t fuck up the ‘id’ param on a hello-world fetch request? What does this stuff look like on anything seemingly more complex (my best guess, it’ll look <i>more complex</i>).
I recommend "next-rpc" [1] which is a small library allows the nextjs client-side pages to call the API function using a type-checked function interface.<p>[1] <a href="https://github.com/Janpot/next-rpc" rel="nofollow">https://github.com/Janpot/next-rpc</a>