DEV Community

Cover image for Strongly-typed destructuring and rest parameters
Carl
Carl

Posted on

Strongly-typed destructuring and rest parameters

Destructuring assignment and rest parameters are awesome and typical in codebases these days. Is it possible to strongly-type these though in TypeScript? Let’s find out.

TypeScript has tuples

Before we figure out how to strongly-type rest parameters, let’s understand tuples. A tuple can be thought of as an array with a fixed number of elements. They are nice for small and obvious data structures. For example, the useState React hook returns a tuple:

const [state, setState] = useState(initialState);
Enter fullscreen mode Exit fullscreen mode

TypeScript lets us define tuples in a type annotation by specifying the type of each element in square brackets. For example:

const tomScore: [string, number]: ["Tom", 70];
Enter fullscreen mode Exit fullscreen mode

Open-ended tuples

What have tuples got to do with rest parameters? Well, we’ll get there eventually.

TypeScript lets us have tuples where we can have a varying number of end elements like below:

["Tom", 70]
["Jane", 70, 60]
["Fred", 70, 60, 80]
Enter fullscreen mode Exit fullscreen mode

We can specify the type for the above example as [string, ...number[]].

Strongly-typing rest parameters

I wonder if we can use an open-ended tuple to strongly-type a rest parameter? Let’s try to do this for the scores parameter in the function below:

function logScores(firstName, ...scores) {
  console.log(firstName, scores);
}

logScores("Ben", 50, 75, 85)  // outputs Ben and [50, 75, 85]
Enter fullscreen mode Exit fullscreen mode

Let’s try this:

function logScores(firstName: string, ...scores: [...number[]]) {
  console.log(firstName, scores);
}
Enter fullscreen mode Exit fullscreen mode

If we think about it, [...number[]] is just number[]. So, this can be simplified to:

function logScores(firstName: string, ...scores: number[]) {
  console.log(firstName, scores);
}
Enter fullscreen mode Exit fullscreen mode

... and if we consume the function:

logScores("Ben", 50, 75, 85)     // okay
logScores("Mike", 90, 65, "65")  // Argument of type '"65"' is not assignable to parameter of type 'number'
Enter fullscreen mode Exit fullscreen mode

Great – it works!

Strongly-typing destructured assignment

Specifying the type on destructured object variables is perhaps not achieved how you might first expect. The following doesn’t specify type annotations for firstName and score:

const fred = { firstName: "Fred", score: 50 };
const { firstName: string, score: number } = fred;
Enter fullscreen mode Exit fullscreen mode

Instead, it specifies names for the destructured variables:

console.log(firstName);  // cannot find name 'firstName'
console.log(score);      // cannot find name 'score'
console.log(string)      // "Fred"
console.log(number)      // 50
Enter fullscreen mode Exit fullscreen mode

We specify the type annotation after the destructured object as follows:

const { firstName, score }: { firstName: string, score: number } = fred;
console.log(firstName);   // "Fred"
console.log(score);       // 50
Enter fullscreen mode Exit fullscreen mode

If we are destructuring a tuple, we specify the tuple type after the destructured tuple:

const tomScore: [string, number]: ["Tom", 70];
const [firstName, score]: [string, number]  = tomScore;
console.log(firstName);   // "Tom"
console.log(score);       // 70
Enter fullscreen mode Exit fullscreen mode

We can specify a type on an open-ended tuple as follows:

const tomScores: [string, ...number[]] = ["Tom", 70, 60, 80];
const [firstName, ...scores]: [string, ...number[]]  = tomScores;
console.log(firstName);   // "Tom"
console.log(scores);      // 70, 60, 80
Enter fullscreen mode Exit fullscreen mode

It is worth noting that often TypeScript cleverly infers the types of destructured elements, but it is good to know how to specify type annotation in the edge cases when it doesn’t.

Wrap up

TypeScript tuples are a convenient way of strongly-typing small and obvious data structures. Open-ended tuples can be used to strongly-type rest parameters. TypeScript generally smartly infers the types of destructured elements for us, but when it can’t, we can simply put a type annotation after the destructured items.

Originally published at https://www.carlrippon.com/strongly-typed-destructuring-and-rest-parameters/ on Oct 29, 2019.

Top comments (0)