Libraries like io-ts and zod take care of this for you. For io-ts for example you just need to define a type guard function that asserts "x is Y" and away you go, you've got a runtime codec; it knows how to "decode" data, and it knows what that represents to the type system at compile-time. You'll generally only need to use the built-in combinators though e.g. `t.type({ prop: t.string })` produces a codec for `{ prop: string }`, and you can get that type at compile-time without effectively rewriting it using `t.TypeOf<typeof myCodec>`.