TypeScript : Using advanced types for expressive code

  • Be expressive with types
  • Declarative over imperative
  • Functional over procedural/OOP
  • Readable names for functions and variables
type Direction = 'N' | 'S' | 'E' | 'W';
type Turn = 'L' | 'R';
type ActionType = Direction | Turn | 'F';
type Navigation = [action: ActionType, value: number];
const t: Turn = 'A'; // Invalid as value A is not a valid Turn
const t: Turn = 'L'; // Valid
/*
F10 ==> [F, 10]
N3 ==> [N, 3]
F7 ==> [F, 7]
R90 ==> [R, 90]
F11 ==> [F, 11]
*/
const lineToNav = (l: string) => [l[0], +l.substr(1)] as Navigation;
// State Transitions
START east 0, north 0, facing east
1. F10 east 10, north 0, facing east
2. N3 east 10, north 3, facing east
3. F7 east 17, north 3, facing east
4. R90 east 17, north 3, facing south
5. F11 east 17, south 8, facing south
Ship movement
east  0, north 0 ==> x :  0, y :  0
east 10, north 0 ==> x : 10, y : 0
east 10, north 3 ==> x : 10, y : 3
east 17, north 3 ==> x : 17, y : 3
east 17, north 3 ==> x : 17, y : 3
east 17, south 8 ==> x : 17, y : -8
type Ship = [x: number, y: number, facing: Direction];
const ship : Ship = [0, 0, E];
type Action = (s: Ship, v: number) => Ship
type ActionMap = Record<ActionType, Action>;
const shipNav: ActionMap = {
// N10 means ActionType is N, and value (v) is 10
// y indicates NORTH/SOUTH
// Moving north means y will increase, x will stay as is
N: ([x, y, f], v) => [x, y + v, f],
S: ([x, y, f], v) => [x, y - v, f],
E: ([x, y, f], v) => [x + v, y, f],
W: ([x, y, f], v) => [x - v, y, f],
L: ([x, y, f], v) => [x, y, takeTurn(f, v, L)],
R: ([x, y, f], v) => [x, y, takeTurn(f, v, R)],
F: ([x, y, f], v) => [
f === E || f === W ? x + v * factor[f] : x,
f === N || f === S ? y + v * factor[f] : y,
f,
],
};
const factor = {E: 1, W: -1, S: -1, N: 1};
function calculate(instructions: Navigation[]){
const [x, y] = instructions.reduce(
(state, [action, value]) => shipNav[action](state, value),
[0, 0, E] as Ship
);
return Math.abs(x) + Math.abs(y);
};
type Snwp = [x: number, y: number, wx: number, wy: number];
type Action<T> = (s: T, v: number) => T;
type ActionMap<T> = Record<ActionType, Action<T>>;
const snwpNav: ActionMap<Snwp> = {
N: ([x, y, wx, wy], v) => [x, y, wx, wy + v],
S: ([x, y, wx, wy], v) => [x, y, wx, wy - v],
E: ([x, y, wx, wy], v) => [x, y, wx + v, wy],
W: ([x, y, wx, wy], v) => [x, y, wx - v, wy],
L: ([x, y, wx, wy], v) => [x, y, ...rotate(wx, wy, v, L)],
R: ([x, y, wx, wy], v) => [x, y, ...rotate(wx, wy, v, R)],
F: ([x, y, wx, wy], v) => [x + wx * v, y + wy * v, wx, wy],
};
Rotation of Waypoints
const rotate = (
wx: number,
wy: number,
value: number,
turn: Turn
): [number, number] => {
if (value == 180) {
return [wx * -1, wy * -1];
}
if ((value === 90 && turn === R)
||
(value === 270 && turn == L)) {
return [wy, -wx];
}
return [-wy, wx];
};
function calculate2(instructions: Navigation[]){
const [x, y] = instructions.reduce(
(state, [action, value]) => snwpNav[action](state, value),
[0, 0, 10, 1] as Snwp
);
return Math.abs(x) + Math.abs(y);
};
Identical calculate and calculat2 functions
function calculate<T extends [number, number, ...any]>(
Merged calculate function.
Code to call the calculate function.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store