5 ms·
The type script version is type checked: http://bit.ly/2lVoSgY http://bit.ly/2lVoSgY type State = { movies: string[] }; type Action = | {type: '
by spion 7y ago
The type script version is type checked: http://bit.ly/2lVoSgY http://bit.ly/2lVoSgY
type State = {
movies: string[]
};
type Action =
| {type: 'AddMovie', movie: string}
| {type: 'RemoveMovie', movie: string}
| {type: 'Reset'}
let defaultState:State = { movies: [] }
let reducer = (state: State, action: Action): State => {
switch(action.type) {
case 'AddMovie': return { movies: [action.movie, ...state.movies] }
case 'RemoveMovie': return { movies: state.movies.filter(m => m !== action.movie) }
case 'Reset': return defaultState;
}
}
/* No need for additional functions! */
let someAction = {type: 'AddMovie' as const, movie: "The End of Evangelion"}
reducer(defaultState, someAction);
The secret is that typescript doesn't actually lack variants - it does have the equivalent of polymorphic variants. Try adding a new action type, you will get a type error for the reducer return type. It won't be a pretty type error, but you can't forget to add a switch case.
- crimsonalucard 7y agoMy mistake, good to know. The syntax really looks like string comparison rather then actual pattern matching.
- spion 7y agoIt is string comparison, although in terms of performance in JS string constant comparison is done by reference so its much faster. I believe this is closer to polymorphic variants than to actual sum types (global namespace like strings, comparison by name). You can tighten it a bit by switching to symbols instead of strings, which would guarantee unique tags, getting closer to classic variants. However TypeScript isn't big on nominal types, and for a good reason: the node module system for example can easily cause two versions of a module to be instantiated in the same program which in turn would cause two different and incompatible instances of the symbols and symbol types. Nevertheless I would still say that FP-like patterns are supported reasonably well.
- crimsonalucard 7y agoIt may be string comparison on the JS level. However If it is string comparison on the TS level then that means it isn't type checked. Are you sure this is what you mean? That means I can add a new case for string "asdasfsdfds" and TS won't throw a type error. The variant type is not exclusively an FP paradigm.
- spion 7y agoIf you hover over 'action.type' in the switch statement, you will see (property) type: "AddMovie" | "RemoveMovie" | "Reset" So you can't add a new case because while the value is a JS string, the type is a restricted subset of string i.e. a union of only 3 possible string constants. Therefore you are right - its not quite a string at the TS level - its a subset of string. p.s. you can make unions of any primitive values in TypeScript: let a:3 | "hello" | true = 3; a = "hello"; // valid a = true; // valid a = false; // not valid! or between any type and other values, really: let a: number | 'NotNumber' = 0; a = 1; // ok a = a - 1; // still ok, assignment narrowed the type to number a = 'NotNumber' // ok a = a - 1; // error! narrowed the type to the constant 'NotNumber', can't subtract anymore a = 'Hi' // ERROR TS is not always that smart about it (compared to Flow), but sometimes it works out quite well :)