11

Some months ago I created a Js library that I published on Npm. Now I would like to rename some functions. I read this post and I think it's very useful.

Suppose I have a file A:

export function function1(param1, param2) {
  return param1 + param2
}

The exported functions that are usable from library users are in index.js file:

export { function1 } from './A'

and I want to rename it as sum(param1, param2).

I create this obsolete function:

function obsolete(newFunction, oldFnName, newFnName) {
  const wrapper = function () {
    console.warn(
      `Obsolete function called. Function '${oldFnName}' has been deprecated, please use the new '${newFnName}' function instead.`
    )
    newFunction.apply(this, arguments)
  }
  wrapper.prototype = newFunction.prototype
  return wrapper
}

Now what I have to do? I suppose I have to modify the A file in this way:

/** @deprecated since version 2.0 */
export function function1(param1, param2) {
  return sum(param1, param2)
}

export function sum(param1, param2) {
  return param1 + param2
}

and add the sum function to the index file:

export { function1, sum } from './A'

And then? How can I use the obsolete function?

whitecircle
  • 237
  • 9
  • 34
  • 2
    Why not use the accepted answer from this question https://stackoverflow.com/a/19525656/2607891? – mdcq Nov 27 '20 at 01:59
  • @philmcole you simply mean something like this ```/** * @deprecated Since version 1.0. Will be deleted in version 3.0. Use sum instead. */ function function1(param1, param2) { console.warn("Calling deprecated function!") sum(param1, param2); }``` – whitecircle Dec 02 '20 at 07:10
  • You seem to have added yet another bounty onto this question. Can you clarify what exactly the existing answers aren't covering for you? – Liam Dec 09 '20 at 16:33

4 Answers4

15

Ok, so this question seems to have been prompted by your comment (now deleted it seems):

Suppose I have a function fun1 that I want to make it deprecated and the new function is fun2. How can I use this obsolete function? It seems interesting

From this question.


So firstly, your getting a JSdoc (/** @deprecated since version 2.0 */) annotation and this function mixed up.

JSDoc is a markup language used to annotate JavaScript source code files

source

So this is only useful if your planning on creating a JSdoc annotation. Which I'm presuming your not. If your using JSdoc then this should work as is?


So ignoring that I'm going to go back to your question on this code.

Looking at the code you could use it like (not 100% sure as I didn't write it):

// the function from the previous question
function obsolete(oldFunc, newFunc) {
  const wrapper = function() {
    console.warn(`WARNING! Obsolete function called. Function ${oldFunc.name} has been deprecated, please use the new ${newFunc.name} function instead!`)
    newFunc.apply(this, arguments)
  }
  wrapper.prototype = newFunc.prototype
  return wrapper
}

// this is the function that is the replacement for obsfunc
function newfunc(){
  console.log('new called');
}

// this is the function that is being made "obsolete"
function obsfunc(p) {
  return obsolete(obsfunc, newfunc)();
}


// when you call the obsolete function it's actually the new function 
// that gets triggered and an error is displayed in the console.
obsfunc();

in my case functions have parameters and a return value

this code doesn't support that. There is no official way to "obsolete" a function in the Js spec. TBH this is the correct (now deleted) answer to that question IMO. So you'll need to write your own solution. It's not really clear what your definition of "obsolete" is either? Decorators is a good option

Liam
  • 27,717
  • 28
  • 128
  • 190
  • Thanks for your answer but in my case functions have parameters and a return value. How can I do in that case? Also, can you explain a bit your code? And yes, I'm using also JSdoc. – whitecircle Nov 20 '20 at 21:06
4

There is no way of "setting" a function as deprecated. You just make a function similar to this:

function function1 () {
obsolete(function1(), "function1", "newFunction1")

}
2

I realize this might not be super useful, but if you are working with babel you might want to look into es7 decorators, they are meant exactly for use-cases like this one. although they still have no native browser support, hence Babel

function obsolete(newFuncGetter) 
{
  return (prototype, key, descriptor) => {
    descriptor.value = (...args) => {
        const newFunc = newFuncGetter();
    
      console.warn(`'${key}' has been deprecated, please use '${newFunc.name}' instead!`);

      return newFunc(...args);
    };
  
    return descriptor;
  };
}

class Foo
{
  static newfunc()
  {
    return 'new called';
  }

    @obsolete(() => Bar.newfunc)
  static obsfunc()
  {
    return 'old value';
  }
}


console.log(Bar.obsfunc());

I discovered babel only allows decorators on classes and their items, but I believe the actual decorators should also allow separate functions

  • p.s. the `newFuncGetter` is needed due to `Bar` otherwise not being available (because it's not yet initialized), so a small workaround is required – Chris Kruining Nov 24 '20 at 11:41
1

The usage of your obsolete function is very simple:

All you have to do is to pass the new function to it:

/** @deprecated since version 2.0 */
export const function1 = obsolete(function function1(param1, param2) {
  return param1 + param2
}, 'function1', 'sum')

One more minor tweak you could add to your obsolete declaration is a name setting so that the obsolete function will look the same as the original (that would avoid breaking anyone's code - or not anyone's?):

function obsolete(newFunction, oldFnName, newFnName) {
  const wrapper = function () {
    console.warn(
      `Obsolete function called. Function '${oldFnName}' has been deprecated, please use the new '${newFnName}' function instead.`
    )
    newFunction.apply(this, arguments)
  }
  wrapper.prototype = newFunction.prototype
  Object.defineProperty(wrapper, 'name', {
    value: oldName,
    enumerable: true,
    configurable: true
  }
  return wrapper
}

However, if you just rename a method, you can avoid redefinition by passing the reference of the new one to the obsolete:

export function sum(param1, param2) {
  return param1 + param2
}

/** @deprecated since version 2.0 */
export const function1 = obsolete(sum, 'function1', 'sum')

With the name fix from above, this will even rename your deprecated version of the new function to its old name, without manually creating a wrapper.

FZs
  • 16,581
  • 13
  • 41
  • 50
  • Thank you.I tried your solutions.The first one (`export const function1=obsolete(function function1(param1, param2){}`) seems not to work cause function1 has 2 parameters but we are exporting it without params.The second one (with the updated obsolete function) seems to work but I want to ask you a probably stupid thing:since I used function1 in many other functions in my lib and everything is tested (Jest), I have to change function1 to sum and export it in the index.ts,right? Eg I have a tests.test.ts file where I test function1,there I have to import and use sum instead of function1,right? – whitecircle Dec 10 '20 at 06:19
  • @whitecircle 1. Everything should work with the original `obsolete` function as well (even with multiple arguments), because `newFunction.apply(this, arguments)` passes over every argument to the original function. 2. Yes, if you deprecate a function, then you should rewrite every part of your library to not use it. I hope it makes more sense now... – FZs Dec 10 '20 at 12:16
  • @whitecircle Did this explanation help? – FZs Dec 11 '20 at 06:09