Hacker News new | ask | show | jobs
by eyelidlessness 1202 days ago
Types are absolutely available at runtime. You just have to start with the runtime.

  const parseStr = (value: unknown): assert value is string => {
    if (typeof value === 'string') return value

    throw new Error(…)
  }

  const str = parseStr(anythingYouCanThrowAtIt)
I can 100% guarantee you str is going to have the same static and runtime type once you’ve checked it, or parsed it from whatever type you’d accept as a string. TypeScript won’t do the parsing for you, because JavaScript doesn’t have clear semantics for runtime casting that anyone wants or would accept. But type guards are exactly the solution to that and incredibly composable. Once you accept that reality and embrace it, getting the static type out of your runtime parser is a single added line of static type code. And all of this is almost exactly equivalent to what languages with runtime casts do, but you have complete visibility into it because you determine how casts behave. There are whole libraries which do this for you so don’t worry about rolling your own unless you have very particular needs. But TypeScript definitely has the facility to align static and runtime types however you see fit. You just need to tell the type system what types the runtime conveys, same as every other aspect of the type system.
1 comments

You seem knowledgeable about TypeScript, so you certainly very well understand what people mean by "types are not available at runtime". Once your typescript is compiled to javascript, there's nothing left of your user-defined types and interfaces. Type guards are a security that is very much a workaround for the lack of types at runtime.
I’m encouraging thinking about the problem from a different angle. If you parse rather than validate[0][1], you have roughly all of the type information you could want in the runtime, and by using type guards the type system will reflect the runtime types.

It’s not a workaround. Every language with runtime types will have some logic devoted to this kind of casting/narrowing. TypeScript rightly doesn’t build it into the compiler because most of the time you don’t want types to have runtime behavior. For internal logic which can be validated statically, runtime types would be an unnecessary overhead. So it’s up to developers to determine where it should be applied. Type guards are explicitly an affordance for that, designed specifically to convey types with runtime casting/narrowing.

If you use good, composable primitives like zod or io-ts or any of several other implementations, your code will typically be almost identical to deriving runtime from types rather than the inverse.

0: https://itnext.io/parse-dont-validate-incoming-data-in-types...

1: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...