1

I have a question about the code below, I am not sure to understand why the second return is valid. Doesn't the first return do the same kind of check ?

type User = {
  email: string
  firstName?: string
  lastName?: string
  first_name?: string
  last_name?: string
} & ({ first_name: string } | { firstName: string }) &
  ({ last_name: string } | { lastName: string })

const formatUserFullName = (firsname: string, lastname: string): string => `${firsname} ${lastname}`

export const computeUserFullName = (member: User): string => {
  let { firstName, lastName } = member
  if (!firstName) {
    firstName = member.first_name
  }
  if (!lastName) {
    lastName = member.last_name
  }

  // This throw an error about firstName and lastName that can be undefined and are not accepted by my method
  return !firstName && !lastName
    ? member.email
    : formatUserFullName(firstName, lastName)

  // This works
  return !!firstName && !!lastName
    ? formatUserFullName(firstName, lastName)
    : member.email
}

Thanks for your help :)

4
  • 1
    The opposite of !!firstName && !!lastName is !(!!firstName && !!lastName) which simplifies to !firstName || !lastName Commented May 11, 2021 at 14:07
  • What Vlaz said! Commented May 11, 2021 at 14:15
  • In !firstName && !lastName, firstName could be 'mark', and lastName could be undefined and it would go to formatUserFullName('mark', undefined). Since you've defined formatUserFullName as taking two strings, not string | undefined, it rightfully rejects that. Commented May 11, 2021 at 14:20
  • Ah yes indeed, it was that simple... Thank you for your responses ! Commented May 11, 2021 at 14:35

1 Answer 1

1

Doesn't the first return do the same kind of check ?

No, it does not. The one that works uses

firstName && lastName

(simplified, since in boolean algebra !!x = x)

and the inverse of that expression is

!(firstName && lastName)

Which according to DeMorgan's law not (A and B) = not A or not B converts to:

!firstName || !lastName

It is easy to check the truth tables for these and see which is the correct operation that produces the opposite results:

firstName lastName firstName && lastName !firstName || !lastName !firstName && !lastName
Truthy Truthy Truthy Falsy Falsy
Truthy Falsy Falsy Truthy Falsy
Falsy Truthy Falsy Truthy Falsy
Falsy Falsy Falsy Truthy Truthy

To transition from boolean algebra back to code, the Falsy value could just be undefined in the case when the property is missing from the member object.

This means that, for example, firstName or lastName is undefined, then the check you do returns the opposite of what you want:

const check1 = (firstName, lastName) => {
  // This works
  return !!firstName && !!lastName
}
const check2 = (firstName, lastName) => {
   // This throw an error about firstName and lastName that can be undefined and are not accepted by my method
 return !firstName && !lastName
}
console.log(
  check1("Fred", undefined),  //false - correct
  !check1("Fred", undefined), //true - expected
  check2("Fred", undefined)   //false - wrong
);
console.log(
  check1(undefined, "Bloggs"), //false - correct
  !check1(undefined, "Bloggs"),//true - expected
  check2(undefined, "Bloggs")  //false - wrong
);

Which shows that TypeScript correctly found a bug in your code. It works correctly when using

return !firstName || !lastName
    ? member.email
    : formatUserFullName(firstName, lastName)

Playground Link

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.