[HN Gopher] An Inconsistent Truth: Next.js and Typesafety ___________________________________________________________________ An Inconsistent Truth: Next.js and Typesafety Author : theobr Score : 62 points Date : 2021-12-02 18:07 UTC (4 hours ago) (HTM) web link (t3.gg) (TXT) w3m dump (t3.gg) | SavantIdiot wrote: | 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. | klibertp wrote: | > with an implicit type contract ( _potentially generated_ ) | through the creation of these files | | 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. | | I can't find it right now, but there was a paper describing how | it works. It's probably somewhere here: | https://github.com/samth/gradual-typing-bib (if you're curious | enough to read many tens of abstracts...) | crate_barre wrote: | 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 _more complex_ ). | theobr wrote: | You should check out the video I link at the end - the most | complex problems have the exact same solutions when your types | are inferred :) | moksly wrote: | Don't you need some pretty solid source data for you to trust | them? | | 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. | | 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? | theobr wrote: | 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. | | 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! | markharrisuk999 wrote: | Is there any reason why you didn't use BlitzJS? Where, I think | this issue is largely solved with Typescript? | terracottage wrote: | The one thing I would ask this person is how big and complex a | system they've built like this. | | 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. | | 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. | theobr wrote: | Without a strongly inferred type system, all of the things | you're describing around "changing everything to change one | thing" are just as much a problem. | | In a well typed system, you can choose how deep you want your | changes to go. | | Let's say you choose to rename a field in db, I.e. "imageUrl" | -> "profilePicURL". Upon making this change, you will receive | type errors on the client consuming it. | | At this point, you can make a choice. You can go address all | the furthest end consumers, which is what you imply is | necessary here. You can just as easily re-shape the data being | returned though. | | return { ...user, imageUrl: user.profilePicUrl } | | I firmly believe we're nearing the "best of both worlds" here | :) | mirekrusin wrote: | 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. | | The difference it makes is illusion of type-safety vs type-safety | this article touches on. | | It's basically mapping of an `unknown` type into known one, at | runtime. | | 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. | | 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. | | [0] https://github.com/appliedblockchain/assert-combinators | | [1] https://github.com/preludejs/refute | the_duke wrote: | That's only partially true for Typescript and requires a lot of | discipline. | | TS is continuously getting better, but it's still possible to | lose type safety with some constructs without the compiler | warning you about it at all. | | The third party typings for JS dependencies are also often not | a 100% correct, especially for rare code paths and edge cases. | | And due to how easy it is to fall back to any, there are also | Typescript native libraries that don't uphold the guarantees | they seem to give. | | Typescript is great, but it's far from bullet proof, and it's | extremely frustrating to deal with bugs that the type system | supposedly should have prevented. | mirekrusin wrote: | We're lucky where no- or very shallow- dependencies for our | backend services are possible. | | I hear from collegues it's different world on f/e. | | It's true there are tons of poor quality libraries - both in | pure js and ts. We try to avoid this transitive dependency | explosion nonsense alltogether. | | Even well known libraries like lodash, which are not | typescript first - are simply too dynamic. If ts doesn't | offer precise types on functions - we avoid those. | | From our experience it looks like it pays off well to put an | extra effort to guarantee that static types are correct and | precise - no dependencies or trimmed to absolute minimum, | rely on ts first code (usually ours), no dynamic fiddling | that can't be expressed in ts, no any, no pretending via | casting/non-null assertions, parsing/runtime-assertions on | io-boundaries etc. Together with other techniques like | branded types typescript can enter critial systems in | enterprices - areas where js would never be allowed to exist. | As you say it requires discipline - but I wouldn't call it "a | lot", not in our case at least, the effort/benefit ratio is | no-brainer in our case and at the end - it's honestly very | pleasant language to work with. | frenchyatwork wrote: | Some of those issues can be remedied by linting rules | (preventing unsafe use of any), but typing of dependencies | can be a headache. Even 1st party typing and Typescript | dependencies can regularly be wrong if type safety was not | important to whoever wrote them. | jensneuse wrote: | 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: | | 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 | | Result? No more double declaration of types. No manual typing. | More info on how this works with NextJS and Typescript here: [0] | | Note, I'm the founder of WunderGraph and obsessed with creating | the perfect developer experience for working with APIs. I | appreciate any feedback. | | [0]: | https://wundergraph.com/docs/overview/features/generated_cli... | kansface wrote: | How do you deal with multiple versions on the back end - like | during a migration, or a partial migration, deploy, etc? | striking wrote: | If you're using GraphQL, you never make a breaking change (or | make that breaking change only after all clients are proven | to have stopped requesting the broken field) | | Tooling exists to prevent you from making this mistake at CI | time (comparing your schema with the one currently in | production) and ensuring an incompatible change is not made | unless explicitly allowed. | theobr wrote: | This is super cool and I'm excited to dig in more | | Any chance there are some demos building a full stack app on | this tech? | jensneuse wrote: | Hey, thanks for you feedback. Yes absolutely! Here's an | example using Apollo Federation, REST APIs and a standalone | GraphQL API: [0] Alternatively, you can also just run | "wunderctl init --template nextjs-starter" to start with the | NextJS template. (Obviously you need to install it first: | yarn global add @wundergraph/wunderctl@latest) | | We're going open source with this solution soon. So, any | feedback is appreciated! You can also join our discord and | shoot questions. =) | | [0]: https://github.com/wundergraph/wundergraph-demo | SlickStef11 wrote: | Super cool! Will be checking out the documentation! ___________________________________________________________________ (page generated 2021-12-02 23:01 UTC)