开发者

How to know a JavaScript function accepts a simple parameter or a destructured object?

Consider these two functions:

const render = (entity) => {
   // function body
}

const render = ({
    entity,
    isAdmin
}) => {
   // function body
}

Now let's say I want to call this function. And if it's accepting a destructured object, I should pass the arguments differently.

render({
    name: 'John', 
    age: 40
})

// or

render({
    entity: {
      name: 'John',
      age: 40
    },
    isAdmin: true
})

Is there a way for me to know if a function accepts destr开发者_JAVA百科uctured object as its parameter?

Please note that render.length returns 1 for both of these functions. And arguments does not help me because it's accessible inside the function, not outside it.

Update.

This is the real scenario.

We have an administration panel (similar to React Admin) that is used by over than 500 projects across many companies. You can imagine that we have thousands of lists to be shown in tabular format.

Now, for each table, developers give us a render function which is a React component to be used for each row of the table.

This is how they use our <List /> component:

const Customers = () => {
    return <List
        // other props like title, headers, etc.
        row={entity => <>
             <td>{entity.name}</td>
        </>}
    />
}

We don't want to force them across thousands of lists to write row={({entity, metadata}) => <><td>{entity.name}</td></>}


It's not clear to me why you would need to do this. The parameters to a function don't and can't change once declared, and you should always know what parameters a function accepts before calling it.

However, just for the sake of it, here's an example of a way you could do it. Stringify the function, then use a regex to match the ({ }) in the parameter list.

const render1 = (entity) => {}

const render2 = ({
    entity,
    isAdmin
}) => {}

const render3 = (arg) => someotherFunc({arg})

function doesDestructure(func){
  return /\({.+}\) =>/gm.test(func.toString().replaceAll("\n", ""))
}

console.log(doesDestructure(render1))
console.log(doesDestructure(render2))
console.log(doesDestructure(render3))

Or a version that will work for all functions:

const render1 = (entity) => {}

const render2 = ({
    entity,
    isAdmin
}) => {}

const render3 = (entity) => subscribe(({entity}) => {  })

function doesDestructure(func){
  return /\({.+}\)/gm.test(func.toString().replaceAll("\n", "").split("=>")[0])
}

console.log(doesDestructure(render1))
console.log(doesDestructure(render2))
console.log(doesDestructure(render3))


Your original question was definitely an XY Problem but now that you've cleared it up, I believe the issue is...

  1. You have a shared component (probably React) that accepts a function property used to allow callers to dictate how a part of the result is rendered (ie table rows). That function looks something like this currently (Typescript syntax for clarity)

    row: (entity: Entity) => JSX.Element
    
  2. You want to introduce a new element to the renderer including some meta-data but you want to use an object (as opposed to multiple args) to avoid

    1. Locked in parameter order
    2. Locked in number of parameters
    row: (obj: MetaData & { entity: Entity }) => JSX.Element
    

To save the sanity of your developers, I would recommend introducing a new callback prop, allowing them to make the change if and when desired

type EntityMeta = MetaData & { entity: Entity };

interface ListProps {
  row?: (entity: Entity) => JSX.Element,
  rowWithMeta?: (row: EntityMeta) => JSX.Element,
}

In your List component, you can then make the decision on which renderer to use

const rowRenderer = useCallback(
  (row: EntityMeta) => props.rowWithMeta?.(row) ?? props.row?.(row.entity),
  [props.rowWithMeta, props.row]
);

return (
  <table>
    {rows.map((row, i) => (
      <tr key={i}>{rowRenderer(row)}</tr>
    ))}
  </table>
);

Personally, the issues you noted regarding point #2 above would not concern me. I would simply construct whatever meta-data you want and pass that as a second parameter

row: (entity: Entity, meta: MetaData)

You can treat that second parameter however you want, just leave the first as purely the entity

return (
  <table>
    {rows.map(({ entity, ...meta }, i) => (
      <tr key={i}>{props.row(entity, meta)}</tr>
    ))}
  </table>
);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜