Home > Back-end >  Decorate Function Call - Trying to Remove Nullability from Signature
Decorate Function Call - Trying to Remove Nullability from Signature

Time:01-23

Here is the corresponding Playground.


Here is a function which I cannot change at all:

function cannotChangeThisFunc<A>(v: (A | null) | ((u: (A | null)) => (A | null))) {
    // cannot change this body
}

Now I created a function which literally has the same signature as cannotChangeThisFunc but disallows all null values:

function cannotChangeThisSignature<A>(v: A | ((u: A) => A)) {
  cannotChangeThisFunc<A>(/* ... */);
}

The goal is that a call to cannotChangeThisSignature should invoke cannotChangeThisFunc with the same value. The following does not work:

function cannotChangeThisSignature<A>(v: A | ((u: A) => A)) {
    // can change this body
    cannotChangeThisFunc<A>(v);
}

Neither does this (besides the fact that would be runtime checking and should be compile time checking):

function cannotChangeThisSignature<A>(v: A | ((u: A) => A)) {
    // can change this body
    if (typeof v === "function") {
        cannotChangeThisFunc<A>((vv: (A | null)) => (v(vv)));
    } else {
        cannotChangeThisFunc<A>(v);
    }
}

Can someone elaborate please?


Here is the corresponding Playground.

CodePudding user response:

There are two problems:

  1. A function given to the one with null (cannotChangeThisFunc) has to be able to deal with null inputs, but the function of the non-null version (cannotChangeThisSignature) cannot do that.
  2. Type A can be a function itself, so (typeof v === "function") does not guarantee that it is a function of type A => A

The first one you can handle by wrapping the function. This is fine, since cannotChangeThisSignature does not receive null input. However, the second one I don't think you can solve except telling typescript that v must be A => A. So basically cheat a bit:

type Fun<A> = ((u: A) => A) // just for readability

function cannotChangeThisSignature<A>(v: A | Fun<A>) {
  if (typeof v === "function"){
    const wrappedV = wrapWithNullHandler(v as Fun<A>) // read v as A => A
    cannotChangeThisFunc<A>(wrappedV);
  } else {
    cannotChangeThisFunc<A>(v);
  }
}

function wrapWithNullHandler<A>(fun: Fun<A>): Fun<A|null> {
  return (aOrNull: (A|null)) => (aOrNull === null) ? null : fun(aOrNull)
}

That means that you could do

const f = () => 42
cannotChangeThisSignature(f) // no ts error but not right either

and you might get an exception. I don't think that is a limitation in typescript, but a language problem, as it is not possible to discern at runtime if a given function is A => A or something else. That means that there is no clean way to this at all.

But when cheating is the only solution anyway, and handling the nulls isn't really necessary, I would just skip that step:

function cannotChangeThisSignature<A>(v: A | Fun<A>) {
  if (typeof v === "function"){
    cannotChangeThisFunc<A>(v as Fun<A|null>); // it' ok, ts
  } else {
    cannotChangeThisFunc<A>(v);
  }
}
  • Related