Search code examples
typescriptoptional-chaining

Typescript optional chaining with check on the last argument


I have an if statement that looks like this:

if ("arg3" in arg1.arg2 && customFunction(arg1.arg2.arg3)) {}

Now any of the arguments can be undefined so I wanted to use optional chaining:

if ("arg3" in arg1?.arg2? && customFunction(arg1.arg2.arg3)) {}

The problem here is I cannot check arg2 with arg2?. or arg2? because Typescript expects an additional argument or thinks I want to use a conditional operator. So I'm wondering if the only way to get this if statement working is with:

if (arg1?.arg2 && "arg3" in arg1.arg2 && customFunction(arg1.arg2.arg3)) {}

I've had several situations where I try to use optional chaining and needed to check the last argument. I'm quite new to programming so I'm happy if somebody can tell me how to write it correctly or can point me to documentation which explains why I cannot or should not have the last argument as optional.

EDIT1: For those wondering about the types of arg1, 2 and 3: In my situation I have a form you can fill to create an automation for a specific process. You have general Information and an Input and Output which can be of type SMB or Email.

the interface looks something like this:

process: {
  processName: string,
  description: string
  input: smbInterface | emailInterface
  output: smbInterface | emailInterface
}

smbInterface: {
  username: string,
  password: string,
  uncPath: string
}

emailInterface: {
  username: string,
  password: string,
  domain: string
}

When submitting the form I check if any of the fields is empty or null. When checking uncPath like this I get an error "is possibly undefined":

if (isEmpty(process.input.uncPath)) {}

So I change it to:

if ("uncPath" in process?.input && isEmpty(process.input.uncPath)) {}

Still getting the undefined error so I change it to:

if (process.input && "uncPath" in process.input && isEmpty(process.input.uncPath)) {}

This works but I wonder if I can simplify it to something like this:

if ("uncPath" in process?.input? && isEmpty(process.input.uncPath)) {}

Obviously with process?.input? Typescript thinks I want to use a conditional operator so I'm looking for another way.

I hope this clears it up a bit. Sorry if I was unclear (and maybe still am).

EDIT2: Apparently my clearer example is missing something which I currently don't know what it is. As VLAZ pointed out in his comment it should be working with:

if ("uncPath" in process?.input && isEmpty(process.input.uncPath)) {}

For now T.J. Crowders answer solves my question of how to check the last argument of an optional chain with nullish coalescing.


Solution

  • As you say, with just ? you're starting the conditional operator.¹ The optional chaining operator is ?. (the . isn't, er, optional).

    There's another problem with that condition, which is that if arg1 is nullish, the result of arg?.arg2 is undefined, and you can't use in on undefined.

    You could use nullish coalescing:

    if ("arg3" in (arg1?.arg2 ?? {}) && customFunction(arg1.arg2.arg3)) {}
    

    but I think I'd rather do what's in your question since it short-circuits earlier:

    if (arg1?.arg2 && "arg3" in arg1.arg2 && customFunction(arg1.arg2.arg3)) {}
    

    but that's only reliable if we know (presumably from type information) that arg1.arg2 won't have a falsy value other than undefined or null.


    ¹ The conditional operator (?:) is a ternary operator (an operator accepting three operands, just like a binary operator accepts two and a unary operator accepts one), and currently it's JavaScript's only ternary operator, but that could change. :-)