[HN Gopher] Playing Sudoku in TypeScript while the type checker ...
___________________________________________________________________
Playing Sudoku in TypeScript while the type checker highlights
mistakes
Author : mjcurl
Score : 125 points
Date : 2024-08-19 21:18 UTC (4 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| paradite wrote:
| Ok can we reduce sudoku to something Turing complete and hence
| prove TypeScript is Turing complete?
| itishappy wrote:
| Issue #14833 - TypeScripts Type System is Turing Complete
|
| https://github.com/microsoft/TypeScript/issues/14833
| brap wrote:
| Does this mean that some types never get resolved? How does
| the type checker handle that?
| anamexis wrote:
| Indeed, the link gives an example of such an infinitely
| recursive type: type Foo<T extends
| "true", B> = { "true": Foo<T, Foo<T, B>> }[T]; let
| f: Foo<"true", {}> = null!;
|
| As for how it's handled, it yields the error "Type
| instantiation is excessively deep and possibly infinite.
| [2589]" I'm not sure what the max stack depth is, though.
| umanwizard wrote:
| Checking a sudoku board always terminates, so no.
| davidsgk wrote:
| An amazing (if a bit flowery) read that highlights more arcane
| stuff you can do with the TS type system: https://www.richard-
| towers.com/2023/03/11/typescripting-the-...
| a_wild_dandan wrote:
| Using TypeScript for the entire stack feels like a superpower.
| The type system is incredible. V8 is fast. The frameworks are
| phenomenal (Next.js, Material UI, etc). The ecosystem is
| enormous, with packages for _everything_. The unified codebases
| save gobs of duplicate code (e.g. ferrying data betwixt client
| /server). I'm not surprised that such an expressive system can
| play Sudoku!
| sva_ wrote:
| > with packages for everything
|
| Which can quickly turn into a crutch if you're not wise about
| it
| brigadier132 wrote:
| One thing I do when I see a dependency I want to use is get
| its github and paste it in this site:
|
| https://codetabs.com/count-loc/count-loc-online.html
|
| Depending on the # of lines of code and how many people use
| it actively I decide between just
|
| * forking it and using the fork as a dependency
|
| * copying the parts i need from it
|
| * or just using it as a dependency
|
| Either way, the dependencies existing is just a boon. Even if
| you don't want to depend on it directly you can use the
| existing work as a guide and implement something yourself.
| leononame wrote:
| For me, it's the opposite. The type system is decent, but it's
| generics can get extremely out of hand, it's not sound, and I
| run into weird type errors with libraries more often than not.
|
| Having no integer types (ok, this isn't something typescript
| could just implement) other than BigInt is another big one for
| me.
|
| That you can just do `as unknown as T` is an endless source of
| pain and sometimes it doesn't help that JS just does whatever
| it wants with type coercion. I've seen React libraries with
| number inputs that actually returned string values in their
| callbacks, but you wouldn't notice this until you tried doing
| addition and ended up with concatenation. Have fun finding out
| where your number turned into a string.
|
| The number of times I've read `... does not exist on type
| undefined` reaches trauma-inducing levels.
|
| TypeScript is as good as it can get for being a superset of JS.
| But it's not a language I have fun writing stuff in (and I even
| fear it on the backend). It has its place, and it's definitely
| a decent language, but I would choose something else for
| frontend if I could, and wouldn't use it on the backend at all.
| I somehow don't trust it. I know people write amazing software
| with it, but YMMV I guess.
| carderne wrote:
| I tend to agree with you but for problem like this one:
|
| > That you can just do `as unknown as T` is an endless source
| of pain
|
| You should be using strict typingcheck/linting rules
| somewhere in your pipeline to make these illegal (or at least
| carefully scrutinised and documented).
| ridiculous_leke wrote:
| Those rules should be enabled by default.
| moritzwarhier wrote:
| ESLint rules that require type information (not just
| stripping types) are prohibitively expensive for larger
| code bases.
|
| As far as I know, there isn't any kind of tsconfig rule
| to disallow this (please correct me if I'm missing
| something here!). So unless you're using tools I don't
| know about, this is kind of a mandatory last bastion of
| "any".
|
| You can disallow any, enable the strictest possible
| null/undefined checks (including
| noUncheckedIndexedAccess). And there's also the assertion
| TS check that normally prevents erroneous type
| assertions.
|
| But "as unknown as MyType" is not preventable by means of
| tsc, as far as I know. Unless there's an option I don't
| know do disable this kind of assertion (or even all
| assertions).
| lolinder wrote:
| How large is too large and what counts as prohibitive?
| We're using lints with types on over a million lines of
| TypeScript and the lints are instant inside of the
| editors. They take a good 10 minutes to run in CI across
| the whole project, but that's shorter than the tests
| which are running in parallel.
| leononame wrote:
| Sure, I agree in general, but I've found that:
|
| 1. If someone is willing to do `as unknown as T`, they're
| probably also just as willing to do `// @ts-ignore`. 2.
| It's not only your own code, it's the libraries you use as
| well. Typings can often be slightly incorrect and you have
| to work around that occasionally.
| mynameisvlad wrote:
| For #1, this is literally what PRs are for. Someone might
| be willing to do it, but it should be stopped before
| merge. If it isn't, you have bigger problems to solve
| than type coercion.
|
| For #2, if it's open source you're welcome to change the
| source or its typings.
| cdaringe wrote:
| Popular libraries tend to get type hygiene issues ironed
| out rather quickly for 90% of the API surface area. For
| this reason, i find that lib selection from npm is much
| easier these days. The heuristic is simple:
|
| 1) has types? 2) has (large) download count? 3) has docs?
|
| After that it's generally smooth sailing. Of course this
| doesnt at all apply to the application codebase being
| applied to, but one of the parent/sibling remarks
| emphasized "madness" and i seek to smooth that over.
|
| Noisy? Yes. Madness? Nah.
| nox101 wrote:
| You can also turn off all warnings in C and C++ (and
| C#?). That's not a flaw in the language it's a flaw in
| code bases and programmers that turn them off.
| tarruda wrote:
| > Having no integer types (ok, this isn't something
| typescript could just implement) other than BigInt is another
| big one for me.
|
| Is that a performance thing? I believe JavaScript VMs can
| specialize/optimize Numbers and BigInts that only contain
| valid 32 int values.
| leononame wrote:
| None of these are performance concerns. Modern JS engines
| are plenty fast for most of my use cases.
|
| It irks me that I can't trust it to be an integer within a
| given range. Especially with Number, I often have the
| sensation that the type system just doesn't have my back.
| Sure, I can be careful and make sure it's always an
| integer, I've got 53 bits of integer precision which is
| plenty. But I've shot myself in the foot too many times,
| and I hust don't trust it to be an integer even if I know
| it is.
|
| As for BigInt, I default to it by now and I've not found my
| performance noticeably worse. But it irks me that I can get
| a number that's out of range of an actual int32 or int64,
| especially when doing databases. Will I get tto that point?
| Probably not, but it's a potential error waiting to be
| overlooked that could be so easily solved if JS had
| int32/int64 data types.
| mewpmewp2 wrote:
| Do you have any more specific examples where it has
| caused a problem not having int type specifically?
|
| I can't really remember having a problem with it myself,
| but maybe your usecases are different.
| leononame wrote:
| Not really. In my parent comment I tried to make clear
| that it's not a limitation for me in real-world scenarios
| I encounter, but still something I feel like being a
| potential class of problems that could be so easily
| solved.
|
| When I really needed dedicated integer types of a
| specific size, e.g. for encoding/decoding some binary
| data, so far I've been successful using something like
| Uint8Array
| throwanem wrote:
| Sound currency arithmetic is a lot harder when you have
| to constantly watch out for the accidental introduction
| of a fractional part that the type system can't warn you
| about, and that can never be safe with IEEE 754 floats.
| (This doesn't just bite in and near finance: go use
| floating-point math to compute sales tax, you'll find out
| soon enough what I mean.)
|
| Bigints solve that problem, but can't be natively
| represented by JSON, so there tends to be a lot of
| resistance to their use.
| baq wrote:
| I'm converting a billing system from js to ts right now.
|
| Looking for a therapist.
| a_wild_dandan wrote:
| You're blaming TypeScript for self-inflicted wounds.
|
| Don't blame the type system that you banished (with `as
| unknown as T`) for not catching you; or for some React
| library having bugs, e.g. an incorrect interface; for not
| defining types and Pikachu-facing when types are `undefined`.
| These traumas are fixed _by using_ TypeScript, not ignoring
| it.
| foota wrote:
| These issues don't exist in languages that aren't built on
| a marsh.
|
| More specifically though, I feel like the way javascript
| libraries with types work is often painful to use and
| that's why people use TS's escape hatches, whereas the same
| thing doesn't happen in languages where everything is built
| on types from the get go.
|
| The same friction is true for example of using C libraries
| from other languages.
| rty32 wrote:
| "don't exist in languages that aren't built on a marsh"
|
| Sure. Last time I checked, JavaScript is the language
| that actually powers the web. If you can get a language
| that isn't built on a marsh along with all the libraries
| to run the web, I'll switch in a second.
|
| In other words, the criticism is simply irrelevant. If it
| works, it works. We don't talk about technologies that
| don't exist.
| skitter wrote:
| We do in fact talk about technologies that don't exist.
| Creating new technologies would be rather difficult
| otherwise.
| leononame wrote:
| Well, the reality of the situation still is that there are
| libraries with incorrect or low quality typings that blow
| up in your face. Me using TypeScript will not make that
| library better, but this problem is still the daily reality
| of using TypeScript. It's not the fault of TS, but still a
| pain you encounter when working with it.
|
| I haven't worked with a language where you can statically
| cast invalid types that easily since C, a language not
| exactly famously known for its safety.
|
| There's a reason `as unknown as T` exists, and it's
| JavaScript's highly dynamic nature and absence of runtime
| types (ignoring classes and primitives). It's an escape
| hatch that is needed sometimes. Sure, within your own
| codebase everything will be fine if you forbid its use, but
| every library call is an API boundary that you potentially
| have to check types for. That's just not great DX
| iainmerrick wrote:
| _I haven 't worked with a language where you can
| statically cast invalid types that easily since C, a
| language not exactly famously known for its safety._
|
| But it's not the same situation at all, is it? If you
| make an invalid cast in C, your program will crash or
| behave in bizarre ways.
|
| If you make an invalid cast in TS, that doesn't affect
| the JS code at all. The results will be consistent and
| perfectly well-defined. (It probably won't do what you
| wanted, of course, but you can't have everything.)
|
| TS is much more like Java than it is like C (but with a
| much nicer type system than either).
| iainmerrick wrote:
| Totally! I really wonder what these libraries people are
| complaining about that have such bad type definitions. In
| my experience TS definitions on the average NPM package are
| generally fairly decent.
| lolinder wrote:
| > it's not sound
|
| This comes up in every one of these threads and I always
| wonder: do you actually experience problems with soundness in
| your regular coding? (Aside from `as unknown`, which as
| others have noted just means you need a linter to stop the
| bad practices.)
|
| It feels like such a theoretical issue to me, and I've yet to
| see anyone cite an example of where it came up in production
| code. The complaint comes off as being more a general sense
| of ickiness than a practical concern.
| harha_ wrote:
| TypeScript is overly complicated and for what? Compile-time
| "safety". It's better than plain JavaScript, though whatever
| library I pull as a dependency and peek inside the
| TypeScript<>JavaScript interface glue, I always find horrors
| beyond my comprehension.
| mynameisvlad wrote:
| > the TypeScript<>JavaScript interface glue
|
| ... the typing file? That's literally the only thing that's
| different and if the original codebase was written in TS then
| is literally the types they defined.
| harha_ wrote:
| Well either the typing glue or the library itself if it's
| written in TypeScript. My point was: the types are too damn
| complicated. Way too complicated for what they achieve.
| Almost always.
| xamuel wrote:
| Compile error messages in a classical typed language: "Error:
| Object of type 'StructA' cannot be assigned to variable of
| type 'StructB'"
|
| Compile error messages in TypeScript when you use a library
| like React: "Error: Cannot reconcile <5 pages of arcane
| gibberish> with <5 pages of different arcane gibberish>"
| SonOfLilit wrote:
| While TypeScript seems to be a nice language, it's ecosystem is
| the JS ecosystem and it is _madness_.
|
| Major versions of some common library breaking backwards
| compatibility released every week mean you need to run as fast
| as you can just to stay in place.
|
| Public opinion can't be relied upon. The most popular ORM
| library just recently added support for native JOINs, and this
| wasn't common knowledge (I almost used it before at had them!).
| The most decent ORM as chosen by some coworkers very
| experienced with the ecosystem still doesn't support subqueries
| (well, there's an escape hatch for writing them in raw SQL...).
| Some marketer-turned-programmer created hundreds of useless
| packages (like, a separate package per ansi color) that all
| require each other, and they are popular enough that if you'll
| run npm ls in your real world project you will find that you
| depend on them.
|
| Having professionally used cargo, pip, even cabal, npm feels
| like the eternal september of open source packages.
| yieldcrv wrote:
| yeah this part is pretty bad
|
| you can get lucky and reach a nirvana state where all your
| dependencies function well in a new project, but 6 months
| later its a disaster like ah you need to upgrade node, but ah
| your transpiler requires the older version of node, but ah
| the semantic versioning was not followed by your type
| definition addendum library and now there were autoupdated
| breaking changes, ah your project only worked with a locked
| package file and if you re-install any package the wrong way
| everything breaks in incomprehensible ways!
|
| I know my way around it though, so yay big bucks and quick
| deployment of greenfield projects
| space_crab wrote:
| I know this isn't realistic for many many scenarios, but if
| you can help it there is a sweet spot where you dedicate
| ~30 minutes to merge weekly dependabot updates and you
| don't run into this problem.
| lolinder wrote:
| Most of the madness that you're describing there isn't
| inherent in the JavaScript ecosystem, it's more to do with
| undisciplined development practices that might be more likely
| to be enabled by JavaScript's flexibility but are by no means
| required in order to participate.
|
| Don't use libraries that aren't stable. Aggressively trim
| dependencies. Lock versions and upgrade intentionally.
| Ideally, use a company registry to cache what you actually
| want to be using.
|
| Every ecosystem has its problems, and JS+NPM's are largely
| that it's too good at making everything too easy, leading to
| an abundance of naive developers building in a naive way.
|
| On the whole I'll take that over the unnecessary barriers in
| other ecosystems (don't get me started on pip...), but it
| definitely requires some discipline to navigate safely.
| __jonas wrote:
| I had this thought with a personal project but I got lost in a
| nightmare of configuration between typescript, node, the
| browser, my bundler etc.
|
| Maybe it's a better time with a framework like Next, which I
| assume comes with the client / server code sharing part
| preconfigured, bun looked promising as well to simplify the
| development setup. But I ended up switching to another language
| for my backend and I feel pretty good about it.
| samwillis wrote:
| This is super cool, love these sort of projects.
|
| It's crazy what you can achieve with typescript types, what about
| inferring sql query results types from a raw sql query string:
| https://github.com/nikeee/sequelts
| prologist11 wrote:
| Any sufficiently advanced type system is indistinguishable from
| Prolog.
___________________________________________________________________
(page generated 2024-08-23 23:00 UTC)