[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)