{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/typescript/guard-clauses-and-never-type/",
  "description": "Master TypeScript's never type and Python's NoReturn for exhaustiveness checking. Flatten nested conditionals with guard clauses for cleaner code.",
  "path": "/typescript/guard-clauses-and-never-type/",
  "publishedAt": "2022-05-22T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "TypeScript",
    "Python",
    "Typing"
  ],
  "textContent": "Nested conditionals suck. They're hard to write and even harder to read. I've rarely\nregretted the time I've spent optimizing for the flattest conditional structure in my code.\nThe following piece mimics the actions of a traffic signal:\n\nThe snippet above suffers from two major issues:\n\n- It contains three contiguous levels of nested conditionals.\n- The conditionals don't cover the case where the return value is undefined.\n- If you add a fourth member to the Signal enum, now the processing function doesn't\n  exhaustively cover all the cases and it won't communicate that fact with you.\n\nWe can leverage _guard clauses_ to fix the first two issues.\n\n> The guard (clause) provides an early exit from a subroutine, and is a commonly used\n> deviation from structured programming, removing one level of nesting and resulting in\n> flatter code: replacing if guard { ... } with if not guard: return; ...\n\nWe can rewrite the earlier snippet as follows:\n\nThis model has a flatter structure and now it's gracefully handling the undefined return\npath. However, the third issue still persists. In an alien world, if someone added a fourth\nmember to the Signal enum, that'd make the conditional flow in the processSignal\nfunction incomplete since it wouldn't be covering that newly added fourth enum member. In\nthat case, the above snippet will execute the final catch-all conditional statement; not\nsomething that we'd want.\n\nTypeScript provides a never type to throw a compilation error if a new member isn't\ncovered by the conditional flow. Here's how you'd leverage it:\n\nIdeally, the assertNever should never be called. Try removing a conditional and see how\nTypeScript starts screaming at you regarding the unhandled case. The assertNever function\nwill also raise a runtime error if any case remains unhandled.\n\nExample in Python\n\nThe same idea can be demonstrated in Python using Python3.10's match statement and\ntyping.NoReturn type.\n\nSimilar to TypeScript, mypy will complain if you add a new member to the enum but forget to\nhandle that in the processor function. Python 3.11 added the Never type and assert_never\nfunction to the typing module. Underneath, Never is an alias to the NoReturn type; so\nyou can use them interchangeably. However, in this case, Never seems to communicate the\nintent better. You may also choose to use the backported versions of the type and function\nfrom the typing_extensions module. Here's how:\n\nFurther reading\n\n- [Guard clause, guard code, or guard statement]\n- [Never type in TypeScript]\n- [Unreachable code and exhaustiveness checking in Python]\n\n\n\n\n[guard clause, guard code, or guard statement]:\n    https://en.wikipedia.org/wiki/Guard_(computer_science)\n\n[never type in typescript]:\n    https://www.zhenghao.io/posts/ts-never\n\n[unreachable code and exhaustiveness checking in python]:\n    https://typing.readthedocs.io/en/latest/source/unreachable.html",
  "title": "Guard clause and exhaustiveness checking"
}