• Activity
  • Votes
  • Comments
  • New
  • All activity
  • Showing only topics in ~comp with the tag "typescript". Back to normal view / Search all groups
    1. What libraries do you use for implementing web forms, if any?

      I recently ran across Modular Forms, which is a new and rather obscure JavaScript library for doing form validation that claims good support for TypeScript (type safety) and low download size. It...

      I recently ran across Modular Forms, which is a new and rather obscure JavaScript library for doing form validation that claims good support for TypeScript (type safety) and low download size. It has variants for a few frameworks like React and Preact.

      I’m wondering what else people use? I ended up writing my own Preact hooks to help out, with the actual validation done using Zod.

      6 votes
    2. unique types in TypeScript using "branding"

      I'm doing a bit of Typescript programming and started using zod for input validation. It has a fair number of convenience methods, one of which is brand(). This creates a unique type, much like...

      I'm doing a bit of Typescript programming and started using zod for input validation. It has a fair number of convenience methods, one of which is brand(). This creates a unique type, much like the newtype operator in some languages. This is despite TypeScript not having unique types by default; TypeScript implements structural typing.

      The technique used to implement unique types has been known for a long time, but it's new to me. You can declare a type with an extra field that doesn't really exist.

      There seem to be several variations on how to do this. They seem to be mostly equivalent, but the ergonomics might differ. (Some might have better compiler errors that others?) The basic requirement is that the imaginary field doesn't get in the way in normal use, but it's incompatible with other types, causing a compiler error if they're mixed.

      One way goes like this:

      declare const brand: unique symbol;
      
      type Brand<T, TBrand extends string> = T & {
        [brand]: TBrand;
      }
      

      It can be used to declare branded types like this:

      type TopicId = Brand<bigint, "TopicId">;
      
      const myTopicId = 123n as TopicId;
      

      This trick relies on the fact that TypeScript's type checking is unsound. We can lie to the type checker. Intersecting T (in this case, bigint) creates a subtype of bigint with an imaginary field. The field's type is declared so the field requires a specific string, so it's going to be incompatible with just about any other type, unless you use Brand to give it the same name on purpose. (The field doesn't actually exist and no string is actually stored there.)

      To create a TopicId value, you use "as" to explicitly downcast bigints to TopicId's. Then you can use them just like a bigint, except that there will be compile errors if you use them wrong.

      A less strict approach is to make the imaginary field optional, described here as "flavoring" but I don't think that's in common use? Then you could assign bignums without doing a cast, but the type is still incompatible with other branded type. This reminds me of how types work in Go.

      11 votes