Repository: JoshuaKGoldberg/ts-api-utils (ts)

Issue:
🐛 Bug: isFalsyType should return true for `0n`
### Bug Report Checklist

- [X] I have tried restarting my IDE and the issue persists.
- [X] I have pulled the latest `main` branch of the repository.
- [X] I have [searched for related issues](https://github.com/JoshuaKGoldberg/ts-api-utils/issues?q=is%3Aissue) and found none that matched my issue.

### Expected

given the literal `0n` type, `isFalsyType(type)` should be true.

### Actual

`isFalsyType(type)` is false.

### Additional Info

This is because the ts api returns a `PseudoBigInt` object (rather than a `bigint` primitive) which is always truthy, causing this check to be incorrect:

https://github.com/JoshuaKGoldberg/ts-api-utils/blob/41cd21d41f445448681dd32308a48a004f4717cb/src/types/utilities.ts#L49-L51

See also https://github.com/typescript-eslint/typescript-eslint/pull/10205/files

Pull request description:
fix: make `isFalsyType()` return `true` for `0n`
<!-- 👋 Hi, thanks for sending a PR to ts-api-utils! 💖.
Please fill out all fields below and make sure each item is true and [x] checked.
Otherwise we may not be able to review your PR. -->

## PR Checklist

- [x] Addresses an existing open issue: fixes #544 
- [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/ts-api-utils/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/ts-api-utils/blob/main/.github/CONTRIBUTING.md) were taken

## Overview
The `type.value` returns a `PseudoBigInt` object rather than an actual bigint for TS legacy support reasons. Therefore, to determine if a literal is a falsy value, we cannot rely on the truthiness of `type.value` alone, and need to check what the actual object is if it's not a primitive.

Patch:
diff --git a/src/types/utilities.ts b/src/types/utilities.ts
index d16daee..a0be288 100644
--- a/src/types/utilities.ts
+++ b/src/types/utilities.ts
@@ -1,6 +1,7 @@
 // Code largely based on https://github.com/ajafff/tsutils
 // Original license: https://github.com/ajafff/tsutils/blob/26b195358ec36d59f00333115aa3ffd9611ca78b/LICENSE
 
+import semver from "semver";
 import ts from "typescript";
 
 import {
@@ -46,8 +47,12 @@ export function isFalsyType(type: ts.Type): boolean {
 		return true;
 	}
 
-	if (type.isLiteral()) {
-		return !type.value;
+	if (typeIsLiteral(type)) {
+		if (typeof type.value === "object") {
+			return type.value.base10Value === "0";
+		} else {
+			return !type.value;
+		}
 	}
 
 	return isFalseLiteralType(type);
@@ -397,3 +402,24 @@ export function symbolHasReadonlyDeclaration(
 export function unionTypeParts(type: ts.Type): ts.Type[] {
 	return isUnionType(type) ? type.types : [type];
 }
+
+/**
+ * TS's `type.isLiteral()` is bugged before TS v5.0 and won't return `true` for
+ * bigint literals. Use this function instead if you need to check for bigint
+ * literals in TS versions before v5.0. Otherwise, you should just use
+ * `type.isLiteral()`.
+ *
+ * See https://github.com/microsoft/TypeScript/pull/50929
+ */
+export function typeIsLiteral(type: ts.Type): type is ts.LiteralType {
+	if (semver.lt(ts.version, "5.0.0")) {
+		return isTypeFlagSet(
+			type,
+			ts.TypeFlags.StringLiteral |
+				ts.TypeFlags.NumberLiteral |
+				ts.TypeFlags.BigIntLiteral,
+		);
+	} else {
+		return type.isLiteral();
+	}
+}


Test patch:
diff --git a/src/types/utilities.test.ts b/src/types/utilities.test.ts
index b69cd83..80c6c3f 100644
--- a/src/types/utilities.test.ts
+++ b/src/types/utilities.test.ts
@@ -173,6 +173,9 @@ describe("isFalsyType", () => {
 		[true, "false"],
 		[true, "0"],
 		[true, "null"],
+		[true, "0n"],
+		[true, "-0n"],
+		[false, "24n"],
 	])("returns %j when given %s", (expected, source) => {
 		const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
 			${source};