DEV Community

Mike Schutte
Mike Schutte

Posted on

Dynamically assigning a function name in javascript

Say you're writing some higher order functions that return other functions. In my case it was a utility that creates a typed hook to consume some context.

I wanted to let the consumer of this utility to have a reasonable name property on the hook returned, not some generic library code name like "useContext".

The first attempt did not work.

const someFunc = () => { ... }
someFunc.name = someConfig.name
Enter fullscreen mode Exit fullscreen mode

You get a nice error at least.

Cannot change read only name error

So then I think to myself πŸ€” Do I know anything about dynamic naming in general in JavaScript?

πŸ’‘

Objects can have dynamic keys! With square brackets, we can stringify runtime values.

{ [someVar]: "woohoo" }
Enter fullscreen mode Exit fullscreen mode

So, to solve my configurable function name problem, I used computed keys to create an object and computed keys again to destructure the function value.

🀯 Right?

const custom = "Jerome"
const { [custom]: someFunc } = { [custom]: () => void 0 }
someFunc.name === "Jerome" // true
Enter fullscreen mode Exit fullscreen mode

^ You can try that example in your browser console.

Pretty neat stuff! Here was my final solution, inspired by this cheatsheet.

import * as React from 'react';

export function assertContext<T>(
  context: T | undefined,
  config: {
    hookName: string;
    providerName: string;
  },
): asserts context is T {
  if (typeof context === 'undefined') {
    throw new Error(`\`${config.hookName}\` must be used within \`<${config.providerName} />\``);
  }
}

export function createUsableContext<T>(config: {
  hookName: string;
  providerName: string;
}): [() => T, React.Context<T | undefined>] {
  const Context = React.createContext<T | undefined>(undefined);

  // assign the function name dynamically by instantaneously assigning and destructuring an object field
  // const useContext = ... would result in the fn name being `useContext`. Not helpful.
  const { [config.hookName]: useContext } = {
    [config.hookName]: (): T => {
      const context = React.useContext(Context);
      assertContext(context, config);
      return context;
    },
  };

  return [useContext, Context];
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
tonivj5 profile image
Toni Villena • Edited

Very smart tricky! Perfect to decorate a method without lost its original name

export function DecorateMethod(options: {}) {
  return (
    target: any,
    propertyKey: string,
    propertyDescriptor: PropertyDescriptor,
  ) => {
    const method = propertyDescriptor.value as UnknownFunction;

    const { [method.name]: decorated } = {
      [method.name]: () => {...},
    };

    propertyDescriptor.value = decorated;
  };
}
Enter fullscreen mode Exit fullscreen mode

Thanks for publish it ❀️