Skip to content

Instantly share code, notes, and snippets.

@andrevmatos
Last active August 12, 2020 02:02
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 andrevmatos/983b0a1be5f09ee7efa842a9348b199d to your computer and use it in GitHub Desktop.
Save andrevmatos/983b0a1be5f09ee7efa842a9348b199d to your computer and use it in GitHub Desktop.
Typescript Tasks

Flexible assert

assert functions and statements are so omnipresent in almost all programming languages in the last couple of decades, it's almost ironic JavaScript/TypeScript not having it built-in. But it's very easy to implement one. Usually, they receive a condition to test as first parameter, and an optional error to throw as second parameter.

We don't want to throw strings (bad practice), so we wanna wrap them with our custom AssertionFailed error class. But we may want to be more descriptive and provide some more details as a second parameter to such class. We don't want to construct these parameters every time an assertion is run, these can be expensive to fetch and would be needed only if the assertion failed. Uff, there's a lot of cases...

Your task is to write an assert function/guard, which can receive the error parameter in multiple ways:

  • an error string, to be fed to our custom class constructor
  • an error instance, to be thrown directly
  • a tuple containing the error message and the details to be passed to our default error constructor
  • a factory function for the error; this factory can:
    • return the error to be thrown
    • throw the error directly

All of these cases should be properly typed. You may choose do this with overloads, but the implementation should be as strictly typed as possible in itself too.

RxJS distinctDictValues operator

RxJS provides a very nice framework for functional reactive programming. But it's as common as it's easy to extend it with operators to extend its functionality and transform an operator stream through a nice declarative pipeline of standard and custom operators.

Your task is to write a pipeable custom operator function or operator factory to transform an observable emitting mappings/dict/records of key: value pairs every time any value changes, into a stream of [key, value] pairs, where a tuple is emitted if and only if its value is different from the value on the same key on the previous emition of the input observable. The operator factory may receive an optional compareFn function, and default to strict equality. Use generics and watch out for memory leaks. You should emit all values at first (since that would be the first time you're seeing each value). Bonus points if the output inference is strictly typed with literal keys and their respective value types.

Example usage:

from([
  { a: 1 },
  { a: 1, b: 2 },
  { a: 3, b: 2 },
  { a: 3, b: 2 },
])
  .pipe(distictDictValues(/* compareFn? */))
  .subscribe(console.log)
// output: ['a', 1], ['b', 2], ['a', 3]

Partial combineReducers

Redux API include a combineReducers function, which allows creating a higher-level reducer function from sub-reducers for slices of the state: each sub-reducer receives and returns only its slice of state, and the resulting state is returned by the combined reducer function. But there's an issue with this function: it is only able to receive a reducer mapping which covers the whole state!

Your task is to write, with proper typing inference, a partialCombineReducers utility function, which is able to receive a reducers mapping for a subset of the state keys, and combine them with their state slices.

Example usage:

function subReducer(state: number, action: Action): number {
  if (action.type === 'increment') state = state + 1;
  return state;
}
// example incomplete: you may require the expected generics to be passed explicitly; bonus points for smarter inference
const rootReducer = partialCombineReducers({ n: subReducer });
expect(rootReducer({ n: 13, s: 'test' }, { type: 'increment' })).toEqual({ n: 14, s: 'test' });

Safe createReducer

Several redux utils offer a createReducer function, which helps create a reducer by mapping action creators to their respective handlers. These usually accept a mapping object from action type literals to the handlers, but require you to explicitly type the action param type (payload, meta, etc), which is error-prone. An alternative is to offer a function to enable adding individual or grouped handlers, either through a method/member of the returned createReducer object or a builder param in a factory function. In either case, it's obviously an error to try to add a handler for an already handled action, but this case is rarely catched at development time.

Your task is to write a createReducer-like helper function, where the developer is able to handle action creators with their respective handlers, get proper inference of the types, and error at compile time when an action is already handled by that reducer. The developer should be able to pass a tuple of actions, to be handled by the same handler. You may also need to declare the action creator helpers, or implement them.

Example usage:

const reducer = createReducer(/*initialState =*/ 0)
  .handle([increment, addOne], (state/*, action*/) => state + 1);
  
function extendReducer() {
  // @ts-expect-error
  return reducer.handle(increment, (state) => ++state);
}

expect(reducer(undefined, increment())).toBe(1);
expect(extendReducer).toThrow(); // should throw at runtime too!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment