Skip to content

Instantly share code, notes, and snippets.

@MikeBild
Created November 10, 2016 10:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikeBild/ad011e8cdb09c015ee91dcdc136b58b1 to your computer and use it in GitHub Desktop.
Save MikeBild/ad011e8cdb09c015ee91dcdc136b58b1 to your computer and use it in GitHub Desktop.
Q&A - I wanna understand TypeScript - Part TypeScript Union Types as Redux Action Tags
// demonstrate
const simulateActions = [
addTodo({text: 'todo 1'}),
addTodo({text: 'todo 2'}),
toggleTodo({index: 0}),
toggleTodo({index: 1}),
];
const actual = simulateActions.reduce(todosReducer, []);
console.log(actual);
// https://blog.mariusschulz.com/2016/11/03/typescript-2-0-tagged-union-types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOOGLE_TODO';
const todo = (type, payload) => ({type, payload});
const addTodo = payload => todo(ADD_TODO, payload);
const toggleTodo = payload => todo(TOGGLE_TODO, payload);
const todosReducer = (state, action) => {
switch (action.type) {
case ADD_TODO:
return [...state, { text: action.payload.text, done: false }];
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index !== action.payload.index) return todo;
return {
text: todo.text,
done: !todo.done,
};
});
default:
return state;
}
}
@MikeBild
Copy link
Author

Agree! I'm not trying to force plain Javascript onto you. Unisono - perfect! I think TypeScript is a good migration language for people there are have a deep affinity to the "Type/Data-Driven" approach. In this case, let's start with TypeScript. A good recommendation going deeper into a weak typed language.

Last thought related to your newly mentioned TypeScript features, because there are very powerful concepts without using transpiler checked contraints based on types.

non-nullable types - yeah nice, but you can und IMHO you should use a more semantical construct named maybe / optional. The interesting part is, you wrote an article about the the same concept to handle failures. We both like it, so why not consistently apply this concept to possible failures like this?

@mariusschulz
Copy link

Types like Result<T>, Maybe<T>, Optional<T>, or Either<TLeft, TRight> all make sense in their own way. However, I find simple sum types to be more lightweight to express using plain union types. For instance, a method that returns either a number or nothing can be typed as follows:

function tryParseInt(input: string): number | null {
    return /^\d+$/.test(input)
        ? parseInt(input, 10)
        : null;
}

The type number | null communicates clearly what the tryParseInt function can return. I don't see a lack of semantic meaning here. Of course, we could instead create a wrapper object:

type Optional<T> =
    { hasValue: true, value: T } |
    { hasValue: false };

function tryParseInt(input: string): Optional<number> {
    return /^\d+$/.test(input)
        ? { success: true, value: parseInt(input, 10) }
        : { success: false };
}

I still favor the union type. It reduces unnecessary complexity, a goal we both want to achieve.

@GregOnNet
Copy link

GregOnNet commented Nov 11, 2016

Hey guys,

you may want to have a look on @mhoyer's project redux-typed-ducks.
The idea is to wrap Actions in a context (action-creator-function) providing better typing-support.
It builds an abstraction layer on top of Actions.

@MikeBild
Copy link
Author

@marius

I prefer an tech/language agnostic construct either(), because you can do it any "lambda" enabled language.

function either(f, g) {
	return function () {
		return f.apply(this, arguments) || g.apply(this, arguments);
	}
}

const gt10 = x => x > 10;
const even = x => x % 2 === 0;
const f = either(gt10, even);

console.log(f(7))
console.log(f(100))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment