ts-union-parser
v0.1.5
Published
A parser generator for discriminated unions.
Downloads
2
Maintainers
Readme
ts-union-parser
A CLI tool for generating parsers from discriminated union TypeScript types.
Use case
Suppose you have the following types defining the shape of data you'll be sending over a WebSocket connection:
type MessageRequest = {
type: 'request';
item: 'state' | 'user-count';
}
type MessageState = {
type: 'state';
state: State;
}
type MessageUserCount = {
type: 'user-count';
count: number;
}
type MessageError = {
type: 'error';
message?: string;
}
type State = {
connections: number;
status: 'ok' | 'err';
}
/**
* This is a "discriminated union". TypeScript can narrow the type of a `Message`
* using the `type` field, which is unique for each of the four types of the union.
*/
type Message = MessageRequest | MessageState | MessageUserCount | MessageError;
When a WebSocket message is received, we often want to parse it and validate that it is the correct shape before attempting to perform any logic using the (potentially ill-formed) data. This script will generate a parser function that does this parsing and validation step. The output given the types above is:
export function parser(data: string): Message {
const _data = JSON.parse(data);
if (typeof _data !== 'object') {
throw new Error('Parsed data is not an object');
}
if (!_data.hasOwnProperty('type')) {
throw new Error('Parsed data does not have "type" field.');
}
switch (_data['type']) {
case 'request':
if (isMessageRequest(_data)) return _data as MessageRequest;
case 'state':
if (isMessageState(_data)) return _data as MessageState;
case 'user-count':
if (isMessageUserCount(_data)) return _data as MessageUserCount;
case 'error':
if (isMessageError(_data)) return _data as MessageError;
default:
throw new Error(
'Parsed data does not contain valid discriminator value.'
);
}
}
function isMessageRequest(data: any): data is MessageRequest {
return (
typeof data === 'object' &&
data['type'] === 'request' &&
(data['item'] === 'state' || data['item'] === 'user-count')
);
}
function isMessageState(data: any): data is MessageState {
return (
typeof data === 'object' &&
data['type'] === 'state' &&
isState(data['state'])
);
}
function isState(data: any): data is State {
return (
typeof data === 'object' &&
typeof data['connections'] === 'number' &&
(data['status'] === 'ok' || data['status'] === 'err')
);
}
function isMessageUserCount(data: any): data is MessageUserCount {
return (
typeof data === 'object' &&
data['type'] === 'user-count' &&
typeof data['count'] === 'number'
);
}
function isMessageError(data: any): data is MessageError {
return (
typeof data === 'object' &&
data['type'] === 'error' &&
(data.hasOwnProperty('message')
? typeof data['message'] === 'string'
: true)
);
}