2

I've got a function to strip null bytes from strings in input data:

export function stripNullBytes(str: string | null): string | null {
  return str?.replace(/\0/g, '') ?? null
}

Some of my input strings can be null, and some can't be (they're coming from a database). Similarly, some of the output strings can be null, and some can't be.

If I call stripNullBytes on one of the strings that can't be null (and so the output can't be null either), Typescript complains that stripNullBytes(cantBeNull) is not of the right type (it's string | null instead of string).

Is there a way I can tell TypeScript this fact about my function? I can use the bang operator (let str: string = stripNullBytes(cantBeNull)!), but is there a more elegant way that removes the possibility of me making a mistake?

2
  • 3
    So you actually want the function type's to be (str: string) => string | (str: null) => null, rather than (str: string | null) => string | null? Have you tried overloads? Commented Nov 27, 2020 at 9:49
  • Didn't know about those, but I just tried them and they work. If you make this an answer I'll accept it. Commented Nov 27, 2020 at 9:56

1 Answer 1

3

Currently your function's type is (str: string | null) => string | null, which means a string input could give null output and vice versa. Instead, it sounds like you want (str: string) => string | (str: null) => null, where a string output always gives a string output and a null input always gives a null output.

This can be achieved with overloads as follows:

export function stripNullBytes(str: string) : string;
export function stripNullBytes(str: null) : null;
export function stripNullBytes(str: string | null): string | null {
  return str?.replace(/\0/g, '') ?? null
}

Note that the third and final definition isn't actually part of the overload list; consumers can only call the function through one of the first two definitions. This means that you cannot call stripNullBytes with something that's string | null. If you wanted to retain this you have to repeat that as an overload, but ensure that the more specific definitions appear first:

[The compiler] looks at the overload list and, proceeding with the first overload, attempts to call the function with the provided parameters. If it finds a match, it picks this overload as the correct overload. For this reason, it’s customary to order overloads from most specific to least specific.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.