Tuples
Tuples are a method of defining a type that has a finite number of unnamed properties, with each property having an associated type. When using a tuple, all of the properties must be provided. This can best be explained in an example, as follows:
let tuple1: [string, boolean];
tuple1 = ["test", true];
tuple1 = ["test"];
Here, we have defined a variable named tuple1
, whose type is defined as an array of types. The first type is a string, and the second type is a boolean. We then assign a value to the tuple1
variable that contains an array with two values, the first of type string and the second of type boolean.
Note that the last line of this code attempts to assign a value to the tuple1
variable that does not have all of the properties that are required. This last line will generate an error as follows:
error TS2741: Property '1' is missing in type '[string]' but required in type '[string, boolean]'
What this error is telling us is that the number and types defined in a tuple must be provided when we assign anything to a tuple.
Tuple destructuring
As tuples use the array syntax, they can be destructured or disassembled in two ways. The first way of destructuring a tuple uses the simple array syntax, as follows:
console.log(`tuple1[0] : ${tuple1[0]}`);
console.log(`tuple1[1] : ${tuple1[1]}`);
Here, we are logging the values of the tuple1
variable to the console by referencing its index within the array, that is, tuple1[0]
and tuple1[1]
. The output of this code is as follows:
tuple1[0] : test
tuple1[1] : true
Here, we can see that we can access each of the values in the tuple by using the array destructuring syntax. Note that the compiler knows that there are only two elements of this array, and if we attempt to access the third value within this tuple, that is, tuple1[2]
, the compiler will generate an error.
Another way of destructuring a tuple is to use the array syntax to create an array of named elements and then assign the value of the tuple to this variable, as follows:
let [tupleString, tupleBoolean] = tuple1;
console.log(`tupleString = ${tupleString}`);
console.log(`tupleBoolean = ${tupleBoolean}`);
Here, we have used the array syntax to create a tuple out of two variable names, that is, tupleString
and tupleBoolean
. We then assign the value of our original tuple, that is, tuple1
, to this array of named variables. We can then use these named variables instead of needing to access them using the standard array syntax, that is, tuple1[0]
. The output of this code is as follows:
tupleString = test
tupleBoolean = true
Here, we can see that the tuple has been correctly destructured into our two named variables, tupleString
and tupleBoolean
.
Using the named variable syntax to destructure tuples is a better way of constructing your code, as you can name the variable according to how it will be used. We will see some practical examples of using tuples in Chapter 9, Using Observables to Transform Data, where we use the RxJS library to initiate multiple API calls to retrieve JSON data.
Optional tuple elements
Note that tuple elements can be marked optional by using the question mark (?
) after the type, as follows:
let tupleOptional: [string, boolean?];
tupleOptional = ["test", true];
tupleOptional = ["test"];
console.log(`tupleOptional[0] : ${tupleOptional[0]}`);
console.log(`tupleOptional[1] : ${tupleOptional[1]}`);
Here, we have defined a tuple named tupleOptional
that consists of two elements, a string, and an optional boolean value. We then assign the value of ["test", true]
to this tuple, and then we assign just the value ["test"]
to this tuple. As the second element has been marked as optional, we do not need to specify it. We then log the values of the tuple elements to the console, using array syntax. The output of this code is as follows:
tupleOptional[0] : test
tupleOptional[1] : undefined
Here, we can see that the tuple value at index 0
has been set to the value of "test"
, but that the tuple value at index 1
is undefined as it was not specified in our last assignment statement.
Tuples and spread syntax
We are also able to use spread syntax to define a tuple that can have a variable number of elements. Consider the following code:
let tupleRest: [number, ...string[]];
tupleRest = [1];
tupleRest = [1, "string1"];
tupleRest = [1, "string1", "string2"];
Here, we are using spread syntax to indicate that the variable named tupleRest
has a number element, followed by a variable number of string elements. We then assign values to this tuple, starting with a single numerical value, and then a numerical value and a variable number of string values. All of these assignments are valid.
Object destructuring
In a similar way to tuples, standard objects can be also be destructured. Consider the following example:
let complexObject = {
aNum: 1,
bStr: "name",
cBool: true
}
let { aNum, bStr, cBool } = complexObject;
Here, we have defined an object named complexObject
that has three properties, aNum
, bStr
, and cBool
. Each of these properties has been assigned a value. We then destructure this object into three separate variables, named aNum
, bStr
, and cBool
, in a similar manner to how we destructured tuples. We can now use these variables as follows:
console.log(`aNum : ${aNum}`);
console.log(`bStr : ${bStr}`);
console.log(`cBool : ${cBool}`);
Here, we are using the aNum
, bStr
, and cBool
variables that we created when destructuring the complexObject
object. The output of this code is as follows:
aNum : 1
objId : 1
objName : name
As we can see from this output, we are able to destructure simple objects into a series of variables, which allows us to access the value of these properties through our standard variable naming syntax.
Note that we are also able to rename the variable names during the destructuring step as follows:
let { aNum: objId, bStr: objName, cBool: isValid }
= complexObject;
console.log(`objId : ${objId}`);
console.log(`objName : ${objName}`);
console.log(`isValid : ${isValid}`);
Here, we are destructuring the complexObject
into a series of variables. Note the use of the colon (:
) in this example. We are using the colon to rename the aNum
property into the objId
variable, using the syntax aNum: objId
. Similarly, the bStr
property is renamed to a variable named objName
, and the cBool
property is renamed to a variable named isValid
. The colon (:
) symbol as used here is not specifying a type as it normally would, but instead is used to rename the variable name used in destructuring.