ソースを参照

re-impl NonEmptyString and add NonBlankString

Yuki Takei 10 ヶ月 前
コミット
c969374186

+ 1 - 0
packages/core/src/interfaces/index.ts

@@ -1,3 +1,4 @@
+export * from './primitive/string';
 export * from './attachment';
 export * from './attachment';
 export * from './color-scheme';
 export * from './color-scheme';
 export * from './color-scheme';
 export * from './color-scheme';

+ 0 - 54
packages/core/src/interfaces/non-empty-string.spec.ts

@@ -1,54 +0,0 @@
-import { describe, expect, it } from 'vitest';
-
-import { isNonEmptyString, toNonEmptyStringOrUndefined } from './non-empty-string';
-
-describe('isNonEmptyString', () => {
-  /* eslint-disable indent */
-  it.each`
-    input         | expected      | description
-    ${'hello'}    | ${true}       | ${'non-empty string'}
-    ${'world'}    | ${true}       | ${'non-empty string'}
-    ${'a'}        | ${true}       | ${'single character'}
-    ${'1'}        | ${true}       | ${'numeric string'}
-    ${' '}        | ${true}       | ${'space character'}
-    ${'   '}      | ${true}       | ${'multiple spaces'}
-    ${''}         | ${false}      | ${'empty string'}
-    ${null}       | ${false}      | ${'null'}
-    ${undefined}  | ${false}      | ${'undefined'}
-  `('should return $expected for $description: $input', ({ input, expected }) => {
-  /* eslint-enable indent */
-    expect(isNonEmptyString(input)).toBe(expected);
-  });
-});
-
-describe('toNonEmptyStringOrUndefined', () => {
-  /* eslint-disable indent */
-  it.each`
-    input         | expected      | description
-    ${'hello'}    | ${'hello'}    | ${'non-empty string'}
-    ${'world'}    | ${'world'}    | ${'non-empty string'}
-    ${'a'}        | ${'a'}        | ${'single character'}
-    ${'1'}        | ${'1'}        | ${'numeric string'}
-    ${' '}        | ${' '}        | ${'space character'}
-    ${'   '}      | ${'   '}      | ${'multiple spaces'}
-    ${''}         | ${undefined}  | ${'empty string'}
-    ${null}       | ${undefined}  | ${'null'}
-    ${undefined}  | ${undefined}  | ${'undefined'}
-  `('should return $expected for $description: $input', ({ input, expected }) => {
-  /* eslint-enable indent */
-    expect(toNonEmptyStringOrUndefined(input)).toBe(expected);
-  });
-});
-
-describe('toNonEmptyStringOrUndefined type safety', () => {
-  it('should maintain type safety with NonEmptyString brand', () => {
-    const validString = 'test';
-    const result = toNonEmptyStringOrUndefined(validString);
-
-    expect(result).toBe(validString);
-    if (result !== undefined) {
-      const _typedResult: typeof result = validString as typeof result;
-      expect(_typedResult).toBe(validString);
-    }
-  });
-});

+ 0 - 12
packages/core/src/interfaces/non-empty-string.ts

@@ -1,12 +0,0 @@
-export type NonEmptyString = string & { readonly __brand: unique symbol };
-
-export const isNonEmptyString = (value: string | null | undefined): value is NonEmptyString => {
-  return value != null && value.length > 0;
-};
-
-export const toNonEmptyStringOrUndefined = (value: string | null | undefined): NonEmptyString | undefined => {
-  // return undefined if the value is null, undefined or empty
-  if (!isNonEmptyString(value)) return undefined;
-
-  return value;
-};

+ 106 - 0
packages/core/src/interfaces/primitive/string.spec.ts

@@ -0,0 +1,106 @@
+import { describe, expect, it } from 'vitest';
+
+import {
+  isNonEmptyString,
+  toNonEmptyStringOrUndefined,
+  isNonBlankString,
+  toNonBlankStringOrUndefined,
+} from './string';
+
+describe('isNonEmptyString', () => {
+  /* eslint-disable indent */
+  it.each`
+    input         | expected      | description
+    ${'hello'}    | ${true}       | ${'non-empty string'}
+    ${'world'}    | ${true}       | ${'non-empty string'}
+    ${'a'}        | ${true}       | ${'single character'}
+    ${'1'}        | ${true}       | ${'numeric string'}
+    ${' '}        | ${true}       | ${'space character'}
+    ${'   '}      | ${true}       | ${'multiple spaces'}
+    ${''}         | ${false}      | ${'empty string'}
+    ${null}       | ${false}      | ${'null'}
+    ${undefined}  | ${false}      | ${'undefined'}
+  `('should return $expected for $description: $input', ({ input, expected }) => {
+  /* eslint-enable indent */
+    expect(isNonEmptyString(input)).toBe(expected);
+  });
+});
+
+describe('isNonBlankString', () => {
+  /* eslint-disable indent */
+  it.each`
+    input         | expected      | description
+    ${'hello'}    | ${true}       | ${'non-blank string'}
+    ${'world'}    | ${true}       | ${'non-blank string'}
+    ${'a'}        | ${true}       | ${'single character'}
+    ${'1'}        | ${true}       | ${'numeric string'}
+    ${' '}        | ${false}      | ${'space character'}
+    ${'   '}      | ${false}      | ${'multiple spaces'}
+    ${'\t'}       | ${false}      | ${'tab character'}
+    ${'\n'}       | ${false}      | ${'newline character'}
+    ${''}         | ${false}      | ${'empty string'}
+    ${null}       | ${false}      | ${'null'}
+    ${undefined}  | ${false}      | ${'undefined'}
+  `('should return $expected for $description: $input', ({ input, expected }) => {
+  /* eslint-enable indent */
+    expect(isNonBlankString(input)).toBe(expected);
+  });
+});
+
+describe('toNonEmptyStringOrUndefined', () => {
+  /* eslint-disable indent */
+  it.each`
+    input         | expected      | description
+    ${'hello'}    | ${'hello'}    | ${'non-empty string'}
+    ${'world'}    | ${'world'}    | ${'non-empty string'}
+    ${'a'}        | ${'a'}        | ${'single character'}
+    ${'1'}        | ${'1'}        | ${'numeric string'}
+    ${' '}        | ${' '}        | ${'space character'}
+    ${'   '}      | ${'   '}      | ${'multiple spaces'}
+    ${''}         | ${undefined}  | ${'empty string'}
+    ${null}       | ${undefined}  | ${'null'}
+    ${undefined}  | ${undefined}  | ${'undefined'}
+  `('should return $expected for $description: $input', ({ input, expected }) => {
+  /* eslint-enable indent */
+    expect(toNonEmptyStringOrUndefined(input)).toBe(expected);
+  });
+});
+
+describe('toNonBlankStringOrUndefined', () => {
+  /* eslint-disable indent */
+  it.each`
+    input         | expected      | description
+    ${'hello'}    | ${'hello'}    | ${'non-blank string'}
+    ${'world'}    | ${'world'}    | ${'non-blank string'}
+    ${'a'}        | ${'a'}        | ${'single character'}
+    ${'1'}        | ${'1'}        | ${'numeric string'}
+    ${' '}        | ${undefined}  | ${'space character'}
+    ${'   '}      | ${undefined}  | ${'multiple spaces'}
+    ${'\t'}       | ${undefined}  | ${'tab character'}
+    ${'\n'}       | ${undefined}  | ${'newline character'}
+    ${''}         | ${undefined}  | ${'empty string'}
+    ${null}       | ${undefined}  | ${'null'}
+    ${undefined}  | ${undefined}  | ${'undefined'}
+  `('should return $expected for $description: $input', ({ input, expected }) => {
+  /* eslint-enable indent */
+    expect(toNonBlankStringOrUndefined(input)).toBe(expected);
+  });
+});
+
+describe('type safety', () => {
+  it('should maintain type safety with branded types', () => {
+    const validString = 'test';
+
+    const nonEmptyResult = toNonEmptyStringOrUndefined(validString);
+    const nonBlankResult = toNonBlankStringOrUndefined(validString);
+
+    expect(nonEmptyResult).toBe(validString);
+    expect(nonBlankResult).toBe(validString);
+
+    // These types should be different at compile time
+    // but we can't easily test that in runtime
+    if (nonEmptyResult !== undefined && nonBlankResult !== undefined) {
+      expect(nonEmptyResult).toBe(nonBlankResult);
+    }
+  });
+});

+ 22 - 0
packages/core/src/interfaces/primitive/string.ts

@@ -0,0 +1,22 @@
+export type NonEmptyString = string & { readonly __brand: unique symbol };
+export type NonBlankString = string & { readonly __brand: unique symbol };
+
+export const isNonEmptyString = (value: string | null | undefined): value is NonEmptyString => {
+  return value != null && value.length > 0;
+};
+
+export const isNonBlankString = (value: string | null | undefined): value is NonBlankString => {
+  return value != null && value.trim().length > 0;
+};
+
+export const toNonEmptyStringOrUndefined = (value: string | null | undefined): NonEmptyString | undefined => {
+  // return undefined if the value is null, undefined or empty
+  if (!isNonEmptyString(value)) return undefined;
+  return value;
+};
+
+export const toNonBlankStringOrUndefined = (value: string | null | undefined): NonBlankString | undefined => {
+  // return undefined if the value is null, undefined or blank (empty or whitespace only)
+  if (!isNonBlankString(value)) return undefined;
+  return value;
+};