Skip to content

Commit fe52a60

Browse files
fix: make isFalsyType() return true for 0n (#545)
<!-- 👋 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.
1 parent 41cd21d commit fe52a60

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

src/types/utilities.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ describe("isFalsyType", () => {
173173
[true, "false"],
174174
[true, "0"],
175175
[true, "null"],
176+
[true, "0n"],
177+
[true, "-0n"],
178+
[false, "24n"],
176179
])("returns %j when given %s", (expected, source) => {
177180
const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
178181
${source};

src/types/utilities.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Code largely based on https://github.com/ajafff/tsutils
22
// Original license: https://github.com/ajafff/tsutils/blob/26b195358ec36d59f00333115aa3ffd9611ca78b/LICENSE
33

4+
import semver from "semver";
45
import ts from "typescript";
56

67
import {
@@ -46,8 +47,12 @@ export function isFalsyType(type: ts.Type): boolean {
4647
return true;
4748
}
4849

49-
if (type.isLiteral()) {
50-
return !type.value;
50+
if (typeIsLiteral(type)) {
51+
if (typeof type.value === "object") {
52+
return type.value.base10Value === "0";
53+
} else {
54+
return !type.value;
55+
}
5156
}
5257

5358
return isFalseLiteralType(type);
@@ -397,3 +402,24 @@ export function symbolHasReadonlyDeclaration(
397402
export function unionTypeParts(type: ts.Type): ts.Type[] {
398403
return isUnionType(type) ? type.types : [type];
399404
}
405+
406+
/**
407+
* TS's `type.isLiteral()` is bugged before TS v5.0 and won't return `true` for
408+
* bigint literals. Use this function instead if you need to check for bigint
409+
* literals in TS versions before v5.0. Otherwise, you should just use
410+
* `type.isLiteral()`.
411+
*
412+
* See https://github.com/microsoft/TypeScript/pull/50929
413+
*/
414+
export function typeIsLiteral(type: ts.Type): type is ts.LiteralType {
415+
if (semver.lt(ts.version, "5.0.0")) {
416+
return isTypeFlagSet(
417+
type,
418+
ts.TypeFlags.StringLiteral |
419+
ts.TypeFlags.NumberLiteral |
420+
ts.TypeFlags.BigIntLiteral,
421+
);
422+
} else {
423+
return type.isLiteral();
424+
}
425+
}

0 commit comments

Comments
 (0)