4 ms·
I work around the issue of `Object.keys` being unsafe by having a function which takes a list of keys and verifies at compile time that it contains all of the k
by webstrand 3y ago
I work around the issue of `Object.keys` being unsafe by having a function which takes a list of keys and verifies at compile time that it contains all of the known keys on the object. Excess keys won't be iterated over, at runtime, but I consider this to be a boon.
https://tsplay.dev/W47y1m https://tsplay.dev/W47y1m
- a_wild_dandan 3y agoI use type guards and the `io-ts` package to get both compile-time and runtime type checking: import * as t from 'io-ts'; /* -------- Runtime type definitions -------- */ export const FooSchema = t.type({ someStringKey: t.string, someNumericKey: t.number, someLiteralKey: t.union([t.literal(1), t.literal(8675309)]) someOtherType: t.type({ anotherNumber: t.number, yetAnotherType: BarSchema }) }) export const BarSchema = t.type({ ... }) /* ---- Run-of-the-mill Typescript types ---- */ export type Foo = t.Typeof<typeof FooSchema>; export type Bar = t.Typeof<typeof BarSchema>; // Static usage export const Baz: Foo = { someStringKey: 5 } // Compile error! Missing keys/etc // Runtime usage export const isFooType = (input: any): input is Foo => FooSchema.validate(input) if (!isFooType(someUserInput)) return server.send(404); You can enforce exact type shape, or just ignore excess keys, etc. It's pretty handy, and saves me so much work maintaining two type systems, a static one for development and a runtime one for production. All of the typical caveats apply: consider runtime performance cost, recognize that deep/nuanced types will require a deeper/nuanced understanding of io-ts, there are limitations, alternative libraries with different tradeoffs, and so on. I like it. :)