| I haven't used fp-ts directly, but I use an adjacent package that declares fp-ts as a peer dependency: io-ts. I've almost exclusively for easier type management during deserialization. In vanilla TypeScript I would have defined an interface and a user-defined type guard to handle deserialization: interface ClientMessage { content: string } function isClientMessage(thing: unknown): thing is ClientMessage { return thing !== null && typeof thing === 'object' && typeof thing.content === 'string' } expect(isClientMessage('nope')).toBeFalse() expect(isClientMessage({ content: 'yup' })).toBeTrue() but user-defined type guards basically duplicate the interface, are prone to error, and can be very verbose. io-ts solves this by creating a run-time schema from which build-time types can be inferred, giving you both an interface and an automatically generated type guard: import { string, type } from 'io-ts' const ClientMessage = type({ content: string }) expect(ClientMessage.is('nope')).toBeFalse() expect(ClientMessage.is({ content: 'yup' })).toBeTrue() Very nifty for my client/server monorepo using Yarn workspaces where the client and server message types are basically just a union of interfaces (of various complexity) defined in io-ts. Then I can just: ws.on('message', msg => { if (ClientMessage.is(msg)) {
// fullfill client's request
} else {
// handle invalid request
}
})Only thing missing is additional validation, which I think can be achieved with more complicated codec definitions in io-ts. |