From 6b0790bf5026471f55618a2303d3a6a5b36cad77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E8=89=B3=E5=85=B5?= Date: Thu, 27 Aug 2026 19:51:21 +0800 Subject: [PATCH 1/2] feat: add common value guard utilities --- src/index.ts | 1 + src/is.ts | 15 +++++++++++++++ tests/is.test.tsx | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/is.ts create mode 100644 tests/is.test.tsx diff --git a/src/index.ts b/src/index.ts index 00665eee..1529803f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,7 @@ export { getTargetScrollBarSize, } from './getScrollBarSize'; export { default as isEqual } from './isEqual'; +export { isNonNullable, isReactRenderable } from './is'; export { default as isMobile } from './isMobile'; export { default as omit } from './omit'; export { default as pickAttrs } from './pickAttrs'; diff --git a/src/is.ts b/src/is.ts new file mode 100644 index 00000000..c2a2bfd7 --- /dev/null +++ b/src/is.ts @@ -0,0 +1,15 @@ +/** + * Check if a value is neither `null` nor `undefined`. + */ +export function isNonNullable(value: T): value is NonNullable { + return value !== undefined && value !== null; +} + +/** + * Check if a value can produce renderable React content. + */ +export function isReactRenderable( + value: T, +): value is Exclude, false | ''> { + return isNonNullable(value) && value !== false && value !== ''; +} diff --git a/tests/is.test.tsx b/tests/is.test.tsx new file mode 100644 index 00000000..e636b9eb --- /dev/null +++ b/tests/is.test.tsx @@ -0,0 +1,32 @@ +import { isNonNullable, isReactRenderable } from '../src'; + +describe('is', () => { + describe('isNonNullable', () => { + it.each([undefined, null])('returns false for nullable value %p', value => { + expect(isNonNullable(value)).toBeFalsy(); + }); + + it.each([false, '', 0, 'text', ])( + 'returns true for non-nullable value %p', + value => { + expect(isNonNullable(value)).toBeTruthy(); + }, + ); + }); + + describe('isReactRenderable', () => { + it.each([undefined, null, false, ''])( + 'returns false for non-renderable value %p', + value => { + expect(isReactRenderable(value)).toBeFalsy(); + }, + ); + + it.each([0, true, 'text', ])( + 'returns true for renderable value %p', + value => { + expect(isReactRenderable(value)).toBeTruthy(); + }, + ); + }); +}); From aa74818c872228d47f2c7d09910db6a994555eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 1 Sep 2026 10:38:09 +0800 Subject: [PATCH 2/2] docs: clarify isReactRenderable behavior --- src/is.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/is.ts b/src/is.ts index c2a2bfd7..eae4d3d7 100644 --- a/src/is.ts +++ b/src/is.ts @@ -6,7 +6,13 @@ export function isNonNullable(value: T): value is NonNullable { } /** - * Check if a value can produce renderable React content. + * Check whether a value should be treated as renderable content. + * + * Returns `false` only for `null`, `undefined`, `false`, and `''`; all other + * values, including `0` and `true`, are treated as renderable. + * + * This is a compatibility-oriented presence check, not a complete React node + * validator. */ export function isReactRenderable( value: T,