Special kind of array in Typescript - Tuple

Vikas yadav - Sep 26 '21 - - Dev Community

In strictly typed programming languages, array is a Data structure of homogeneous data types with fixed length. In contrast JavaScript is dynamic. In here, array can have elements of heterogeneous data type and length can vary.

In JavaScript:

const elements = ['rick', 23, false];

const len = elements.length; // len = 3

elements.push({name: 'morty'}); 

const changedLen = elements.length; // changedLen = 4
Enter fullscreen mode Exit fullscreen mode

With Typescript, we can restrict that and force arrays to have homogeneous data type what I mean is this.

In Typescript:

const elements: string[] = ['rick', 'morty'];

const len = elements.length; // len = 2
Enter fullscreen mode Exit fullscreen mode

Now if we try to push number or any other data type other than string in elements then Typescript will yell at us.

const elements: string[] = ['rick', 'morty'];

elements.push(1) // Error
/**
* Error: Argument of type 'number' is not assignable to 
* parameter of type 'string'.
*
*/
Enter fullscreen mode Exit fullscreen mode

Even though Typescript enforces the type but length is still not fixed. We can push another element of type string in elements array.

const elements: string[] = ['rick', 'morty'];

const len = elements.length; // len = 2

elements.push('summer')

const changedLen = elements.length; // changedLen = 3
Enter fullscreen mode Exit fullscreen mode

What if our requirement changes like this:

Requirement 1:

  • An Array with type number, boolean and string only.

Solution

Well! that is easy, we can use union type with array in Typescript like this:

const elements: Array<number|boolean|string> = ['summer'];

elements.push(23); // ok
elements.push(true); // ok 

console.log(elements) // ["summer", 23, true] 

elements.push({name: 'morty'}) // Not OK : Error
/**
* Error: Argument of type '{ name: string; }' is not 
* assignable to parameter of type 'string | number | 
* boolean'.
*/
Enter fullscreen mode Exit fullscreen mode

One point to note here is:

The sequence of the data type is not fixed as we defined during the declaration. What it means that, we can push number, boolean and string in any order.

For example, This is also perfectly valid and OK with TypeScript:

const elements: Array<number|boolean|string> = [true];

elements.push(23); // ok
elements.push('summer'); // ok 

console.log(elements) // [true, 23, "summer"] 
Enter fullscreen mode Exit fullscreen mode

By Array<number|boolean|string>, we only narrowed the type and told Typescript that this collection should only have elements of type number, boolean and string. The order can be anything. Typescript do not mind as long as the type is one of the declared types.

Requirement 2 :

  • An array with a fixed number of items
  • type of elements are fixed at each index
  • The type of elements need not be same at all the index

What did you just say An array with a fixed number items ??

loki-wtf

And it can have different type at different index? oh okkkk......

oh-okay-simpsons

Solution

Actually this is possible with new type called tuple in Typescript.

Tuple - Special kind of Array

As per official docs:

Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same.

Tuple fulfils all the requirements described above. Let's see how can we define a tuple.

/**
* let's define a info of character id, name and activeStatus
*/
 const info: [number, string, boolean] = [33, 'Rick' , true];

Enter fullscreen mode Exit fullscreen mode
  • An array with a fixed number items

Just by doing this, now we fixed number of elements in info i.e. 3. So now if you try to access the element at index 4 Typescript will yell at you.

 const info: [number, string, boolean] = [33, 'Rick' , true];

const item = info[4] // error
/**
* Tuple type '[number, string, boolean]' of length '3' has no 
* element at index '4'.
*/

// In contrast if we declare an array with union type like
// below, it will be ok to access the element at index 4 

const arr: Array<number|string|boolean>
                 = [33, 'Rick' , true];
const arrItem = arr[4] //undefined
Enter fullscreen mode Exit fullscreen mode
  • Type of elements are fixed at each index

By defining [number, string, boolean], we have fixed the type of elements at each index. Typescript will infer the type from tuple.

 const info: [number, string, boolean] = [33, 'Rick' , true];
 const item1 = info[0] // type number
 const item2 = info[1] // type string
 const item3 = info[2] // type boolean

 // In contrast, array with union type 

const info: Array<number| string| boolean>
                 = [33, 'Rick' , true];

// type of items can be either string, number or boolean

 const item1 = info[0] // type string | number | boolean
 const item2 = info[1] // type string | number | boolean
 const item3 = info[2] // type string | number | boolean

Enter fullscreen mode Exit fullscreen mode

Advantage of doing this is, I can get all the methods available to string for item2.

Screenshot 2021-09-25 at 1.25.58 PM

  • The type of elements need not be same at all the index

The type of elements in tuple can be same as well as different:

const a: [number, string, boolean] = [33, 'Rick' , true];
const b: [string, string, string] = ['Mr', 'Rick' , 'alive'];
Enter fullscreen mode Exit fullscreen mode

Practical example:

You might be thinking, it looks great but where do we use it.

One of the examples that I can think of is in our custom hooks where we have to return an array consisting values of different data type. Take for example useToggle custom hook

import { useCallback, useState } from "react";

export const useToggle = (
  intialValue: boolean = false
): [boolean, () => void] => {
  const [state, setState] = useState(intialValue);

  const setToggle = useCallback(
       () => setState((flag) => !flag), 
   []);

  return [state, setToggle];
};
Enter fullscreen mode Exit fullscreen mode

Here we have to return current status of toggle and a function to change the status. That's why, the return type is a tuple [boolean, () => void].

If we simply return an array, and assign the second argument i.e. setter function to onClick, Typescript will throw a compile time error as the return type is union of boolean and () => void .

Type 'boolean | (() => void)' is not assignable to type 

'((event: MouseEvent<HTMLButtonElement, MouseEvent>) 
    => void) 
   | undefined'.
Enter fullscreen mode Exit fullscreen mode

Screenshot 2021-09-25 at 1.59.21 PM

You can checkout these examples here:

Thank you for reading.

Read my other TypeScript articles

Follow me on twitter

References

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player