From 162133fd1960af45bcde120b912e8f34c370b1ad Mon Sep 17 00:00:00 2001 From: Konrad Dziekonski Date: Tue, 14 Jan 2025 20:52:05 +0100 Subject: [PATCH] feat(isDigit): add isDigit type --- src/string/isDigit/algo.ts | 14 +++++ src/string/isDigit/index.test.ts | 88 ++++++++++++++++++++++++++++++++ src/string/isDigit/index.ts | 11 ++++ 3 files changed, 113 insertions(+) create mode 100644 src/string/isDigit/algo.ts create mode 100644 src/string/isDigit/index.test.ts create mode 100644 src/string/isDigit/index.ts diff --git a/src/string/isDigit/algo.ts b/src/string/isDigit/algo.ts new file mode 100644 index 0000000..45e7631 --- /dev/null +++ b/src/string/isDigit/algo.ts @@ -0,0 +1,14 @@ +import type { IsStringLiteral } from "@predicates" + +export type _IsDigit = [T] extends [ + `${number}`, +] + ? true + : false + +export type IsDigit_Back = + IsStringLiteral extends true + ? [T] extends [`${number}`] + ? true + : false + : false diff --git a/src/string/isDigit/index.test.ts b/src/string/isDigit/index.test.ts new file mode 100644 index 0000000..04b333b --- /dev/null +++ b/src/string/isDigit/index.test.ts @@ -0,0 +1,88 @@ +import { + describe, + it, + expectTypeOf, +} from "vitest" + +import type { + _IsDigit, + IsDigit_Back, +} from "./algo" + +describe("_IsDigit type tests", () => { + it("should return true for string literals that match numeric patterns", () => { + type T1 = _IsDigit<"0"> // "0" extends `${number}` => true + type T2 = _IsDigit<"123"> // "123" extends `${number}` => true + type T3 = _IsDigit<"001"> // "001" extends `${number}` => true + type T4 = _IsDigit<"1.2"> // "1.2" extends `${number}` => true (valid numeric string) + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it("should return false for string literals that are not numeric", () => { + type T1 = _IsDigit<""> // empty string => not a valid number + type T2 = _IsDigit<"abc"> // not a numeric string + type T3 = _IsDigit<"1a2"> // contains non-numeric characters + type T4 = _IsDigit<"one"> // obviously not numeric + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it("should return false for non-literal or non-string types", () => { + // For a generic string, TS cannot confirm it's strictly `${number}` literal. + type T1 = _IsDigit + type T2 = _IsDigit // definitely not a string literal + type T3 = _IsDigit<123> // same, not a string + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) +}) + +describe("IsDigit_Back type tests", () => { + it("should return true for numeric string literals", () => { + type T1 = IsDigit_Back<"0"> + type T2 = IsDigit_Back<"123"> + type T3 = IsDigit_Back<"001"> + type T4 = IsDigit_Back<"1.2"> + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it("should return false for non-numeric or non-literal strings", () => { + type T1 = IsDigit_Back<"abc"> // not numeric + type T2 = IsDigit_Back<"1a2"> // contains letters + type T3 = IsDigit_Back<""> // empty string => not a valid number + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it("should return false for generic string or non-string types", () => { + type T1 = IsDigit_Back // not a literal + type T2 = IsDigit_Back // not a string at all + type T3 = IsDigit_Back<123> // also not a string + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it("should return false when given a union of string literals (one not digit)", () => { + // If IsStringLiteral is strictly checking for a single literal type, + // then a union of two string literals is not "a single literal." + type T1 = IsDigit_Back<"123" | "abc" | "234"> + expectTypeOf().toEqualTypeOf() + }) + it("should return true when given a union of string literals (all digits)", () => { + // If IsStringLiteral is strictly checking for a single literal type, + // then a union of two string literals is not "a single literal." + type T1 = IsDigit_Back<"123" | "345" | "1214"> + expectTypeOf().toEqualTypeOf() + }) +}) diff --git a/src/string/isDigit/index.ts b/src/string/isDigit/index.ts new file mode 100644 index 0000000..4fde746 --- /dev/null +++ b/src/string/isDigit/index.ts @@ -0,0 +1,11 @@ +/** + * A type that returns true if a param is a stringified number, and false otherwise. + * + * @template Str - The string literal type to reverse. + * @returns {string} - Returns the reversed string. + */ + +import type { _IsDigit } from "./algo" + +export type IsDigit = + _IsDigit