Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to both destructure a function parameter, and keep a named reference to the parameter?

In react stateless functional components we'll commonly write something like this:

export function MyCompoment({
    title, 
    foo, 
    bar
}) {
    return <div> title: {title}, ...</div>
}

Where we're immediately destructuring the props object into its variables.

Now I'm using a Material-UI makeStyles hook, and I'm also using TypeScript, and the way I'm currently using it looks like this.

const useStyles = makeStyles((theme : Theme ) =>({
     root: ({foo} : MyComponentProps) => ({
         content: foo
     })
}); 

interface MyComponentProps  {
    title: string; 
    foo: string; 
    bar: string; 
}
export function MyCompoment({
    title, 
    foo, 
    bar
} : MyComponentProps) {
    const classes = useStyles({
        title,
        foo, 
        bar
    }); 
    return <div> title: {title}, ...</div>
}

And you can see the problem - I have to repeat the props variable names to pass into the classes.

The best way I can think to avoid this, is write it this way:

export function MyCompoment(props: MyComponentProps) {
    const {
       title, 
       foo, 
       bar
    }  = props; 
    const classes = useStyles(props); 
    return <div> title: {title}, ...</div>
}

But that's a little messier than I was wanting.

I was wondering if it's possible to do something like:

export function MyCompoment({
       title, 
       foo, 
       bar
    }  = props : MyComponentProps) {

    const classes = useStyles(props); 
    return <div> title: {title}, ...</div>
}

Pretty picky I know, just wondering.

like image 566
dwjohnston Avatar asked May 29 '19 03:05

dwjohnston


1 Answers

No (mostly)


From the grammar:

It's hard to prove a negative, so I had to go to the grammar.

Typescript, as far as I'm aware, does not provide any extra destructuring power that javascript does not, so I'll just answer this for javascript.

In the ES6 grammar, there's no way for a single function parameter to be both destructed and given a name in the parameter list, in one parameter.

If you look at the ES6 grammar for FormalParameter, which is one of the things in an argument list, you'll find it can only be a BindingElement, which is either a SingleNameBinding or a BindingPattern — not both. Binding patterns can only do destructuring and single name bindings can only assign to one value, so there's no way to do both.

(Note the grammar I linked is just a gist someone put on github. I don't think someone would post a misleading ES6 grammar on github, but if you're skeptical you can always check the less convenient official grammar.)

It's possible there's some other wild way to do this that I missed, but I've never seen it before and would be very surprised.


If you really want to...

You're "best way you can think of" is the best way I can think of too. You should do that.

I don't like answering "no", though, so if you really want to get it all in the parameter list, you can do something unpleasant instead. If you do:

function assignAndDestructure(props, { foo, bar } = props) {
    // props, foo, and bar will all be assigned
}

which kind of fits your criteria. However, it creates an optional second parameter the caller can abuse to mess up your destructuring. You can hide it by assigning it to a type that doesn't have that parameter in Typescript, but it's still risky.


In summary, there's no good way to do it, but there is a bad way. Go with your mentioned "best you can think of."

like image 150
Half Avatar answered Sep 21 '22 06:09

Half