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