typescripttypesnullunion-typesdisjoint-union

Typescript: Checking disjoint union with extra null-checks


I'm using a disjoint union to represent different states a payment can be in our typescript codebase. So I assert it's type first with an if in order to access it's properties, but when I check multiple states together alongside some null-checking condition, the compiler gets angry at me and I can't tell the reason.

type Payment = {
  tag: "Pending",
  link: string;
} | {
  tag: "Expired",
  link: string | null;
} | {
  tag: "Other"
};

function test(payment: Payment): string {
  if (payment.tag === "Pending" || (payment.tag === "Expired" && payment.link !== null)) {
    // This fails with:
    // Type 'string | null' is not assignable to type 'string'.
    return payment.link;
  }

  return "this has no link";
}

A simple solution is checking these conditions separately:

// This is fine now
function test(payment: Payment): string {
  if (payment.tag === "Pending") {
    return payment.link;
  }

  if (payment.tag === "Expired" && payment.link !== null) {
    return payment.link;
  }

  return "this has no link";
}

But I don't want to have to copy-paste the code in two separate ifs. Why can't typescript handle this simple case of null-checking? Am I doing something wrong?


Solution

  • Apparently the TS compiler is just not that smart.

    This solution moving around parentheses works:

      if ((payment.tag === "Pending" || payment.tag === "Expired") && payment.link !== null) {
        return payment.link;
      }
    

    which in turn here can be simplified to:

      if (payment.tag !== "Other" && payment.link !== null) {
        return payment.link;
      }