diff --git a/.changeset/fix-coerce-nan.md b/.changeset/fix-coerce-nan.md new file mode 100644 index 0000000..a07ff78 --- /dev/null +++ b/.changeset/fix-coerce-nan.md @@ -0,0 +1,5 @@ +--- +"@bomb.sh/args": patch +--- + +Fix coerce() returning NaN for non-numeric digit-starting strings diff --git a/src/index.ts b/src/index.ts index 524e527..d3c9515 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,8 +61,10 @@ const coerce = (value?: string, type?: "string" | "boolean" | "array") => { if (!value) return value; if (value.length > 3 && BOOL_RE.test(value)) return value === "true"; if (value.length > 2 && QUOTED_RE.test(value)) return value.slice(1, -1); - if ((value[0] === "." && /\d/.test(value[1])) || /\d/.test(value[0])) - return Number(value); + if ((value[0] === "." && /\d/.test(value[1])) || /\d/.test(value[0])) { + const n = Number(value); + if (!isNaN(n)) return n; + } return value; }; diff --git a/test/flags.test.ts b/test/flags.test.ts index 61e01d2..66bbe35 100644 --- a/test/flags.test.ts +++ b/test/flags.test.ts @@ -258,6 +258,26 @@ describe("special cases", () => { }); }); +describe("coerce NaN guard", () => { + it("version-like string as positional is kept as string", () => { + expect(parse(["3.7.1"])).toEqual({ _: ["3.7.1"] }); + }); + + it("digit-prefixed non-numeric string as positional is kept as string", () => { + expect(parse(["1abc"])).toEqual({ _: ["1abc"] }); + }); + + it("version-like string as flag value is kept as string", () => { + expect(parse(["--version", "3.7.1"])).toEqual({ _: [], version: "3.7.1" }); + }); + + it("valid numbers still coerce correctly", () => { + expect(parse(["42"])).toEqual({ _: [42] }); + expect(parse([".5"])).toEqual({ _: [0.5] }); + expect(parse(["3.14"])).toEqual({ _: [3.14] }); + }); +}); + describe("boolean flags", () => { it("should handle long-form boolean flags correctly", () => { const input = ["--add"];