#Higher order null check

6 messages · Page 1 of 1 (latest)

onyx pivot
#

I have a function that checks if a dependency is null which wraps another function to guarantee the dependency will not be null:

type Foo = {
  doSomething: (s: string) => void;
};

let foo: Foo | null = null;

const checkNull =
  <T extends (...args: any[]) => void>(fn: T) =>
  (...args: Parameters<T>) => {
    if (!foo) {
      throw new Error('foo is null');
    }

    fn();
  };

const func = checkNull((s: string) => {
  foo.doSomething(s);
  /* The above line has the following TypeScript error:
  /* 'foo' is possibly 'null'.ts(18047)
  */
});

foo = {
  doSomething: (s: string) => {
    console.log(s);
  },
};

I believe the anonymous function I'm passing to checkNull should never encounter foo that is null, but TypeScript thinks it can possibly still be null here.

Am I missing a way to pass to this wrapped function that we have already null checked foo? For example, the following would work:

const func = (s: string) => {
  if (!foo) {
    throw new Error('foo is null');
  }
  
  foo.doSomething(s);
  /* The above line foo can never be null */
}

I'm currently refactoring some vanilla JS to TS, so this is a contrived example, but I'm trying to find a way to pass the null check type guarantee without a major refactor of the underlying logic.

vocal token
#

There really isn't any way to do that; a function can only do whatever it's signature says it does, and there isn't really a way for the checkNull signature to express something about a variable that isn't part of the signature at all.

#

Assertion function can indicate things about the arguments passed into them, but even then it doesn't work with callbacks:

drowsy slateBOT
#
function foo(x: string | null, cb: () => void ): asserts x is string {}

function example(input: string | null) {
  foo(input, () => { 
    // doesn't work, assertion isn't applied to callback
    input.toUpperCase()
//  ^^^^^
// 'input' is possibly 'null'.
  });
  input.toUpperCase(); // works due to assertion
}
vocal token
#

The main small[ish] tweak you could make would be for checkNull to pass foo into fn as an additional argument.

onyx pivot
#

thanks that makes sense. I think a refactor here is probably the way to go to guarantee null checking anyway