Types in Typescript

Typescript Exercism Data Structures Map

Typescript land is my current location in my learning journey and as usual I'm using Exercism to get my hands dirty with the new language that I'm learning.

The exercise Resistor Color Duo was a good introduction to Typescript's types.

Problem description

The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.

The band colors are encoded as follows:

  • Black: 0
  • Brown: 1
  • Red: 2
  • Orange: 3
  • Yellow: 4
  • Green: 5
  • Blue: 6
  • Violet: 7
  • Grey: 8
  • White: 9

How can we represent this information in Typescript?

Array

Documentation - Everyday Types

Like the numbers are restricted to the range 0..9, we can create array like this:

const bandColors = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"]

where each the index of each color is its value.

This solve the problem but does not answer questions like:

  • How to limit the resistance value to the range 0..9?
  • How to store information where the keys aren't consecutive?

Tuple

Documentation - Object Types

This allow us to answer the question: How to limit the resistance value to the range 0..9?

type colorType = [string, string, string, string, string, string, string, string, string, string] 

// OK
const colors: colorType = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"]

// Wrong: The tuple colorType can't have more than 10 elements
const colors: colorType = ["black", "purple", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"]

Index Signature

Documentation - Object Types

This approach allow us to answer the question: How to store information where the keys aren't consecutive?

We can define a type Resistors where the keys are going to be the resistor colors and the values are going to be the resistance values. This way we can store any kind of info and we aren't limited by the numeric and consecutive indexes of an array or tuple.

type BandColor = 'black' | 'brown' | 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'grey' | 'white';
type ResistanceValue = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

**type Resistors = {
  [color in BandColor]: ResistanceValue;
}**

const resistors: Resistors = {
  'black': 0,
  'brown': 1,
  'red': 2,
  'orange': 3,
  'yellow': 4,
  'green': 5,
  'blue': 6,
  'violet': 7,
  'grey': 8,
  'white': 9,
};

Technically we could have more than 10 keys, but the values should be duplicate because if it's a value not specified in the BandColor type then an error will be throw.

This guide is very instructive

Index Signatures

Map

The previous solution is good but we are basically recreating a Map, then it's better use it directly instead of reinventing the wheel.

const resistors: Map<BandColor, ResistanceValue> = new Map([
  ['black', 0],
  ['brown', 1],
  ['red', 2],
  ['orange', 3],
  ['yellow', 4],
  ['green', 5],
  ['blue', 6],
  ['violet', 7],
  ['grey', 8],
  ['white', 9],
])

Solution

You can check my final solution here: https://exercism.io/tracks/typescript/exercises/resistor-color-duo/solutions/045c8237cf2f4c9db56d8f11ad39bd5c

type BandColor = 'black' | 'brown' | 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'grey' | 'white';
type ResitanceValue = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

const resistors: Map<BandColor, ResitanceValue> = new Map([
  ['black', 0],
  ['brown', 1],
  ['red', 2],
  ['red', 2],
  ['orange', 3],
  ['yellow', 4],
  ['green', 5],
  ['blue', 6],
  ['violet', 7],
  ['grey', 8],
  ['white', 9],
])

export class ResistorColor {
  private colors: string[]
  
  constructor(colors: string[]) {
    if (colors.length < 2)
      throw new Error('At least two colors need to be present')

    this.colors = colors
  }

  value = (): number => {
    return parseInt(
      this.colors
        .slice(0, 2)
        .reduce((accumulator: string, currentColor: string): string => {
          return accumulator + (resistors.get(currentColor as BandColor)?.toString() ?? '')
        }, '')
    )
  }
}

The property colors is to keep a copy of the values that we received in the constructor.

The method value is interesting. First we create a copy of the two first elements of the array with this.colors.slice(0, 2) . Then we reduced that array to the sum of its values with the function

reduce((accumulator: string, currentColor: string): string => {
    return accumulator + (resistors.get(currentColor as BandColor)?.toString() ?? '')
}, '')

References