coproduct v3.0.0
coproduct
A small library aims to improve better tagged-unions/discriminated-unions supporting for TypeScript
Benefits
- Small bundled size(just 1kb)
- Easy to use with just a few apis to learn
- Improving Type-Safety for your TypeScript project via exhaustive pattern-matching
Installation
yarn add coproduct
npm install --save coproductUsage
For redux app
// state type
type CounterState = {
count: number;
};
// action type
type CounterAction =
| {
type: 'incre';
}
| {
type: 'decre';
}
| {
type: 'increBy';
step: number;
}
| {
type: 'decreBy';
step: number;
};
// reducer with match
const counterReducer = (
state: CounterState,
action: CounterAction
): CounterState => {
return match(action).case({
incre: () => ({
...state,
count: state.count + 1,
}),
decre: () => ({
...state,
count: state.count - 1,
}),
increBy: ({ step }) => ({
...state,
count: state.count + step,
}),
decreBy: ({ step }) => ({
...state,
count: state.count - step,
}),
});
};
// reducer without match
const counterReducer = (
state: CounterState,
action: CounterAction
): CounterState => {
if (action.type === 'incre') {
return {
...state,
count: state.count + 1,
};
} else if (action.type === 'decre') {
return {
...state,
count: state.count - 1,
};
} else if (action.type === 'increBy') {
return {
...state,
count: state.count + action.step,
};
} else if (action.type === 'decreBy') {
return {
...state,
count: state.count - action.step,
};
}
throw new Error(`Unexpected action: ${action}`);
};Basic usage
import { match } from 'coproduct';
export type Option<T> = {
type: 'Some',
value: T
} | {
type: 'None'
}
export const None = {
type: 'None' as const;
}
export const Some = <T>(value: T) => ({
type: 'Some' as const,
value,
});
const show = <T>(data: Option<T>) => {
return match(data).case({
Some: data => `some: ${data.value}`,
None: () => 'none',
});
};
const value0 = Some(1);
const value1 = None;
expect(show(value0)).toBe('some: 1');
expect(show(value1)).toBe('none');
// you can use if/else to match manually if you want
const show = <T>(data: Option<T>) => {
if (data.type === 'Some') {
return `some: ${data.some}`;
} else if (data.type === 'None') {
return 'none';
}
throw new Error(`Unexpected data: ${data}`);
};You don't need to define your own option type, coproduct has built-in Option and Result.
import { match, Option, Some, None, Result, Ok, Err } from 'coproduct';
const show = <T>(data: Option<T>) => {
return match(data).case({
Some: data => `some: ${data.value}`,
None: () => 'none',
});
};
expect(show(Some(1))).toBe('some: 1');
expect(show(None)).toBe('none');
const showResult = <T>(result: Result<T>) => {
return match(result).case({
Ok: data => `ok: ${data.value}`,
Err: error => `err: ${error.info}`,
});
};
expect(showResult(Ok(1))).toBe('ok: 1');
expect(showResult(Err('error'))).toBe('err: error');Api
match(data).case(patterns)
match(data).case(patterns) perform exhaustive pattern-matching for data, every case in data should has its own visitor function.
Note: you can use _: () => R as default handler for unmatched case.
createMatch(tagField) => match
You can create your own match function with tagField to match your data.
The default match of coproduct was created via createMatch('type')
const match = createMatch('tag');
type Data =
| {
tag: 'a';
value: string;
}
| {
tag: 'b';
value: number;
};
const handleData = (data: Data) => {
return match(data).case({
a: data => `a: ${data.value}`,
b: data => `b: ${data.value}`,
});
};
handleData({ tag: 'a', value: 'hello' }); // 'a: hello'
handleData({ tag: 'b', value: 1 }); // 'b: 1'Some(value)
Some(value) return the value with the Some<T> case of Option Type.
None
None is the value with the None case of Option Type
Ok(value)
Ok(value) return the value with the Ok<T> case of Result Type.
Err(message)
Err(message) return the value with the Err<E> case of Result Type.
Caveats
- The name
$tagis reserved for$tagproperty of tagged object, it can't be used as a tag name. - The symbol
_can't be used as a tag name since it's a reserved filed incoproductas placeholder fordefaultcase.
Contribution Guide
# test
npm run test
# build
npm run buildAuthor
š¤ Jade Gu
- Twitter: @guyingjie129
- Github: @Lucifier129
š¤ Contributing
Contributions, issues and feature requests are welcome!
Feel free to check issues page.
Show your support
Give a āļø if this project helped you!
š License
Copyright Ā© 2022 Jade Gu.
This project is MIT licensed.