0

I have the following function definitions

function getX1(): string | undefined { ... }

function getX2(): string { ... }

function myFunc(x: string | undefined): MyClass | undefined {
    if (x === undefined) return undefined;
    return { ... };
}

function consume(val: MyClass) { ... }

myFunc is guaranteed to only return undefined if x is undefined.

Scenario 1:

const x1: string | undefined = getX1();
const res1 = myFunc(x1);
if (res1) {
    consume(res1);
}

Here, I want the caller to make sure the result res1 is not undefined before continuing.

Scenario 2:

const x2: string = getX2();
const res2 = myFunc(x2);
consume(res2);

Here, I don't want to add the same overhead for the caller. This snippet currently gives me a compiler error. I can't figure out how to avoid the undefined check without using !. Is there a way to have typescript enforce this rule for me?

2
  • Please provide code for getX1/getX2 or share code in TS playground Commented Nov 10, 2020 at 19:58
  • 1
    The implementation of getX1 and getX2 doesn't matter much. getX1 returns string | undefined, getX2 returns string. I've added this clarification above. Commented Nov 10, 2020 at 20:00

2 Answers 2

4

Here's one way to tackle this issue:

function myFunc(x: string): MyClass;
function myFunc(x: undefined): undefined;
function myFunc(x: string | undefined): MyClass | undefined {
  if (x === undefined) return undefined;
  return {  };
}

You can define the exact return types separately (undefined returning undefined and string returning MyClass). This helps the compiler understand what happens when you pass a certain value to it.

Here's a playground link

and you can find an explanation on Overloads here (thanks @jcalz for pointing this out)

LE:

Ideally you should be able to do something like this, using conditional types:

function myFunc<T extends string | undefined>(x: T): T extends string ? MyClass : undefined {
  if (x === undefined) return undefined;
  return {  };
}

Unfortunately, there's an open issue exactly about this problem and typescript incorrectly narrows conditional types. Until that is fixed (still isn't as of 4.1) you should use function overloads.

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

3 Comments

Consider providing a link to the documentation on overloads in TS, which is what the answer is using.
Hmm seems like this doesn't work when the parameter being passed in is of type "string | undefined". It needs to either be "string" or "undefined" for this to work. Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'.ts(2769)
Adding a third overload that explicitly takes string | undefined seems to have fixed it!
0

The solution will work, just to give you a hint on how to improve readability in typescript.

Use typeof

Use always typeof === "undefined" if you want to check for undefined values explicit. There are some edge cases where prop === undefied is not working as you would expect.

Avoid undefined

If you use undefined you probably want also to check for null values in your control flow.

Prefer null instead of undefined it is more explicit and easier handle in control flows.

Here a quick suggestion for your code example


function getX1(): string { ... }

function getX2(): string { ... }

function myFunc(x: string): MyClass {
    if (typeof x === "undefined") return null;
    return { ... };
}

function consume(val: MyClass) { ... }

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.