from Hacker News

Extreme explorations of TypeScript's type system

by joshuakgoldberg on 6/27/22, 6:13 PM with 60 comments

  • by fishtoaster on 6/27/22, 8:05 PM

    You can do some truly silly things with sufficiently ridiculous uses of typescript. I built a typecheck-time spell checker[0] in it such that:

      import { ValidWords } from "./spellcheck";
      
      // Typechecks cleanly:
      const result: ValidWords<"the quick brown fox."> = "valid";
      
      // Throws a type error
      const result: ValidWords<"the qxick brown fox."> = "valid";
    
    
    [0] https://github.com/kkuchta/TSpell
  • by kevingadd on 6/27/22, 8:10 PM

    When doing fancy things with typescript types, be really careful - it's possible to accidentally construct typescript types that will increase your tsc compile times by multiple seconds and the tooling for troubleshooting this is nonexistent. A tiny change to one codebase I work on made compile times go from 300ms to something like 7 seconds and it took me something like 14 hours of grepping and manually bisecting source code to find the cause - tsc was O(N * N * N) trying all possible types for a string literal to determine whether any of them were valid matches, and someone had defined a very fancy string literal type.

    When this happens, typescript language integration (like in vs code or sublime text) will suddenly fall over and stop working correctly, and it'll be near impossible to figure that out too.

    Our build uses rollup to invoke tsc and as it happens their profiling system doesn't actually measure how long tsc takes to run - the time is unaccounted :) So in general, be aware that 'typescript is taking a long time to compile' is a blind spot for this whole ecosystem and if you hit it you're going to have to work hard to fix it.

  • by dllthomas on 6/27/22, 7:46 PM

    > If you do find a need to use type operations, please—for the sake of any developer who has to read your code, including a future you—try to keep them to a minimum if possible. Use readable names that help readers understand the code as they read it. Leave descriptive comments for anything you think future readers might struggle with.

    Also, as you start getting complicated logic in your types, you need to test your types; make sure they admit things they should admit and reject things that they should reject. Ideally these tests can also serve some role as examples for your documentation.

  • by sir_pepe on 6/27/22, 7:15 PM

    To try and limit one's use of operations on types, as suggested in the article, is not really great advice in my opinion. Sure, you would not want to actually implement and use a VM in types, but distilling rules about a program into types and then deriving the actual interfaces and signatures from those rules with operations on types? That's quite powerful.

    TypeScript's type annotations are really a DSL embedded into JavaScript. And they can, and, depending on the problem at hand, should be treated as such.

  • by tobr on 6/27/22, 7:22 PM

    > You have to wonder whether you could implement TypeScript itself in that language...

    I also wonder if you could compile TypeScript to TypeScript types? After all, you want your type manipulation code to be typesafe.

  • by theogravity on 6/27/22, 6:57 PM

    This is a great list. I feel I'm only scratching the surface when it comes to Typescript, and it would be awesome to have a place where we can see advanced examples of Typescript usage like this.

    I've seen many projects where the typing is done so well that it can infer and include all the data I've fed into the TS-defined functions / classes, which is great for IDE autocompletion.

  • by evolveyourmind on 6/27/22, 7:57 PM

    Some other type-only TS projects:

    - RegExp matching through types: https://github.com/desi-ivanov/ts-regexp

    - Lambda calculus through types: https://github.com/desi-ivanov/ts-lambda-calc

    - Brainfuck through types: https://github.com/susisu/typefuck

  • by pjnz on 6/28/22, 1:20 AM

    I did some fiddling around building a graphql layer with a bunch of complex types. Basically this was trying to encode all the various GraphQL rules into the type system itself e.g. if a resolver takes arguments, ensure that a schema of the correct type is provided as an object etc. I also built a client that would take a schema and ensure you used it correctly at compile time.

    Example of the code: https://github.com/pj/typeshaman/blob/main/packages/graphql/...

    Documentation is incomplete, unfortunately I had to get a job. I started working on encoding all of SQL as well.

  • by DonatelloTHM on 6/28/22, 2:26 AM

  • by adamddev1 on 6/27/22, 7:23 PM

    Are there (m)any other languages with type systems as flexible and powerful as Typescript's?
  • by bichiliad on 6/28/22, 1:32 PM

    If you're going down the rabbit hole of writing complex types, check out Dan Vanderkam's "The Display of Types" post[0]. It goes into how types show up in editors and error messages and such, and has a bunch of tricks for improving type readability. I really wish I read it sooner!

    [0]: https://effectivetypescript.com/2022/02/25/gentips-4-display...

  • by joshuakgoldberg on 6/27/22, 6:13 PM

    TypeScript's type system is Turing Complete: meaning it has conditional branching (conditional types) and works with an arbitrary huge amount of memory. As a result, you can use the type system as its own programming language complete with variables, functions, and recursion. This blog post is a starting list of a bunch of the shenanigans TypeScript developers have pushed the type system to be able to do.
  • by tgv on 6/28/22, 9:06 AM

    Does anybody have a document that describes all type constructions in typescript? The "official" handbook doesn't seem complete. Some time ago, I tried to use tuples in types, but I couldn't figure out the correct syntax. I found some vaguely similar examples on stackoverflow, so it seems some people did get that information.
  • by mbrodersen on 6/29/22, 12:48 AM

    Advanced type systems are fun to play with. But unfortunately some people get carried away and build a mountain of unnecessary complexity that other developers then have to deal with. A bit like Lisp macros. It is fun to implement your own type system and DSLs in Lisp. But the result is likely to be completely unmaintainable by anybody else and yourself a year later when you have forgotten how it works. I have seen the same happen with templates in C++. Developers that spend weeks having fun building a mountain of template hell to solve a problem that could have been solved in a few hours without template magic. As with everything else, keeping an eye on the benefit/cost ratio is key.
  • by truth_seeker on 6/28/22, 10:04 AM

    Very very cool. Thanks for sharing. SQL Database engine source code exceeded my expectation.