Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructuring a function parameter object and ...rest

Tags:

I'd like to write a function that takes an object parameter and captures all the remaining parameters in a variable. The goal is to allow the function to receive named parameters (as opposed to positional parameters), some of them optional, and set default values in the function. So, in pseudo code something like this:

interface IMyProps {
  a: string
  b?: number
}

const myfun1 = (p: {a: string, b?:number, ...rest}) => {
  const {a, b = 'hello'} = p;
}

What would be the best way to achieve this in Typescript 2.0?

like image 964
marko Avatar asked May 11 '18 08:05

marko


People also ask

What is difference between Destructuring and rest operator?

Destructuring is used to create varibles from array items or object properties. Spread syntax is used to unpack iterables such as arrays, objects, and function calls. Rest parameter syntax will create an array from an indefinite number of values.

What is parameter Destructuring?

Destructuring in JavaScript is used to unpack or segregate values from arrays or properties from object literals into distinct variables, thus it allows us to access only the values required.

Can we Destructure function in JavaScript?

JavaScript Object Destructuring is the syntax for extracting values from an object property and assigning them to a variable. The destructuring is also possible for JavaScript Arrays.

Can I use object Destructuring?

You can use the object destructuring assignment to swap the values of two or more different variables. The snippet above used direct object destructuring to reassign the firstName and website variables with the values of the object literal on the right-hand side of the assignment operator.


2 Answers

You can use destructuring assignment directly in the function arguments:

interface IMyType { 
    a: number;
    b: number;
    c: number;
    d: number;
    [k: string]: number; // use this if you don't know the name of the properties in 'rest'
}

const obj: IMyType = { a: 1, b: 2, c: 3, d: 4 }

// Normal destructuring
const { a, b, ...rest } = obj;
console.log(rest); // {c: 3, d: 4}

// Directly in function arguments
const fn = ({ a, b, ...rest }: IMyType) => { console.log(rest); }

console.log(fn(obj)); // {c: 3, d: 4}
like image 132
klugjo Avatar answered Oct 21 '22 09:10

klugjo


UPDATE:

Given the PR at microsoft/TypeScript#28312, you could now use generics, like this:

const myfun1 = <T extends IMyProps>(p: T) => {
    const { a, b = 'hello', ...rest } = p;
    a; // string
    b; // number | 'hello'
    rest; // const rest: Pick<T, Exclude<keyof T, "a" | "b">>
}

in which the rest variable is inferred to be of type Omit<T, "a" | "b">, which is probably what you want.


Pre TS4 answer

I think object destructing with the rest operator in TypeScript corresponds to the index signature on the type of the destructured object if you have pulled off all the explicitly named properties already. That means you would have the same restriction where the types of the rest properties would have to be at least as wide as the union of all the explicitly labeled properties. In your case, you could extend IMyProps with an index signature like this:

interface IMyPropsWithIndex {
  a: string
  b?: number
  [k: string]: string | number | undefined
}

since the type of a is string and the type of b is number | undefined. You could add more stuff to the union but you couldn't make it something narrower like string. If that's okay with you, then the way to do destructuring would be something like this:

const myfun1 = (p: IMyPropsWithIndex) => {
  const { a, b = 'hello' , ...rest } = p;
  a; // string
  b; // number | 'hello'
  rest; // {[k: string]: string | number | undefined}
}

If you inspect the types of the variables, you get something like the above.


Playground link to code

like image 22
jcalz Avatar answered Oct 21 '22 09:10

jcalz