(no title)
NTARelix | 3 years ago
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.
No comments yet.