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
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
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'.
*
*/
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
What if our requirement changes like this:
Requirement 1:
- An
Array
with typenumber
,boolean
andstring
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'.
*/
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 pushnumber
,boolean
andstring
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"]
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 ??
And it can have different type at different index? oh okkkk......
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];
- 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
- 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
Advantage of doing this is, I can get all the methods available to string
for item2
.
- 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'];
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];
};
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'.
You can checkout these examples here:
Thank you for reading.
Read my other TypeScript articles
Follow me on twitter
Top comments (11)
Tuple is not Array in general definition. Tuple is implemented as Array in JS as there is no Tuple in the language. In Python(and many other) for example tuple syntax is different -
tuple = (1,2)
and array isarr = [1,2]
. The title of this article is misleading.Thank you for pointing out. I have changed the title and included Typescript as well. This article is written in context of Typescript. Yes other languages like Python have different syntax. But in Typescript we have this syntax for now. As @urielsouza29 pointed out we have already a proposal for native support for Tuple in Javascript which will have a different syntax.
A small mistake: arrays are not fixed length, not necessarily. Arrays in higher level type systems are usually dynamic in length. So the terms "array" and "vector" are often interchangeable.
But yes an array is a primitive homogenous collection. A tuple is a product type. Being a product type makes it have a constant number of different fields.
JavaScript/typescript is very dynamic. Which makes it very cool to do things like these - but I'm struggling to see where this will be beneficial? Having a class/interface with those properties on it is much easier to work with, pushing this type into a normal array. It also improves readability. Just because you can doesn't mean you should. I don't know - it's just my personal opinion. If you spend too much time trying to figure out what the previous developer did, you're already failing in writing maintainable code.
Thanks for your valuable insight.
I partially agree with you @jaspeling . We can return a class/interface. But at the same time it is type introduced in Typescript. And it is very common while writing custom hooks to return an array as I explained in my example. In React
useState
uses same kind of pattern.Ah, see I have no react knowledge. If that is a pattern used in a very well known framework, I guess I can see the value in a post like this.
I see. Yes, it can be bit confusing at first. When I switched to react and saw this pattern I had the same feeling 😊
As if JavaScript hasn't done enough damage already... a tuple is not an array.
Stage 2 TC39 Tuple github.com/tc39/proposal-record-tuple
Thank you for the link @urielsouza29
:)
Thanks for mentioning. Yes better DX :)