is-kit 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -111,6 +111,69 @@ This is the core idea of `is-kit`:
111
111
  2. Compose them.
112
112
  3. Reuse them anywhere TypeScript narrowing matters.
113
113
 
114
+ ## 🧭 Guard Composition Guide
115
+
116
+ When writing reusable guards with `is-kit`, start from the library primitives:
117
+ use `define` for custom runtime checks, and use logic combinators such as
118
+ `and`, `or`, and `not` when combining existing guards. This keeps the result
119
+ reusable as a named guard and preserves the type-level intent in hover,
120
+ completion, and generated declarations.
121
+
122
+ ```ts
123
+ import {
124
+ and,
125
+ define,
126
+ isNil,
127
+ isNumber,
128
+ isString,
129
+ nullish,
130
+ or,
131
+ predicateToRefine
132
+ } from 'is-kit';
133
+
134
+ const isId = or(isString, isNumber);
135
+ const isNullishString = nullish(isString);
136
+
137
+ const isSlug = define<string>(
138
+ (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
139
+ );
140
+
141
+ const isPositiveNumber = and(
142
+ isNumber,
143
+ predicateToRefine<number>((value) => value > 0)
144
+ );
145
+
146
+ isNil(null); // true
147
+ isNil(undefined); // true
148
+ isId('user-1'); // true
149
+ isNullishString(undefined); // true
150
+ isSlug('release-110'); // true
151
+ isPositiveNumber(1); // true
152
+ ```
153
+
154
+ Prefer these forms when generating or reviewing code:
155
+
156
+ ```ts
157
+ declare const value: unknown;
158
+
159
+ // Prefer
160
+ const isId = or(isString, isNumber);
161
+ const isMaybeName = nullish(isString);
162
+ const isSlug = define<string>(
163
+ (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
164
+ );
165
+
166
+ // Avoid
167
+ const isId = (value: unknown) => isString(value) || isNumber(value);
168
+ const isMaybeName = (value: unknown) => value == null || isString(value);
169
+ const isSlug = (value: unknown): value is string =>
170
+ isString(value) && /^[a-z0-9-]+$/.test(value);
171
+ ```
172
+
173
+ For AI agents or repository-wide conventions, copy
174
+ [docs/agent-rules.md](./docs/agent-rules.md) into the consumer repository's
175
+ agent instructions.
176
+
114
177
  ## ⌚ A 30-second Mental Model
115
178
 
116
179
  If you are new to the library, these are the pieces to remember:
package/dist/index.d.mts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * is-kit guard authoring guide:
3
+ * - Use define<T>(...) for reusable custom runtime checks.
4
+ * - Use and, andAll, or, and not to compose existing guards instead of
5
+ * hand-written boolean wrappers.
6
+ * - Use nullable, optional, or nullish to widen guards for nullish values.
7
+ * - Use struct or typedStruct for object-shape guards.
8
+ */
1
9
  type Predicate<T> = (value: unknown) => value is T;
2
10
  type Refinement<A, B extends A> = (value: A) => value is B;
3
11
  type Guard<T> = Predicate<T>;
@@ -55,6 +63,34 @@ type InferSchema<S extends SchemaShape<S>> = Simplify<{
55
63
  } & {
56
64
  readonly [K in OptionalSchemaKeys<S>]?: InferSchemaField<S[K]>;
57
65
  }>;
66
+ /**
67
+ * Extracts optional keys from an object type.
68
+ */
69
+ type OptionalObjectKeys<T> = {
70
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
71
+ }[keyof T];
72
+ /**
73
+ * Extracts required keys from an object type.
74
+ */
75
+ type RequiredObjectKeys<T> = Exclude<keyof T, OptionalObjectKeys<T>>;
76
+ /**
77
+ * Schema shape checked against an existing object type by `typedStruct`.
78
+ */
79
+ type TypedStructShape<T extends object> = {
80
+ readonly [K in Extract<RequiredObjectKeys<T>, string>]-?: Predicate<T[K]>;
81
+ } & {
82
+ readonly [K in Extract<OptionalObjectKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
83
+ };
84
+ /**
85
+ * Keeps a schema type while rejecting keys outside the expected shape.
86
+ */
87
+ type NoExtraKeys<S, Shape> = S & {
88
+ readonly [K in Exclude<keyof S, keyof Shape>]: never;
89
+ };
90
+ /**
91
+ * Field set accepted by `typedStruct` for a target object type.
92
+ */
93
+ type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
58
94
 
59
95
  /**
60
96
  * Asserts that a value satisfies a guard or refinement, otherwise throws.
@@ -69,12 +105,18 @@ declare function assert<A, B extends A>(refine: Refine<A, B>, value: A, message?
69
105
 
70
106
  /**
71
107
  * Wraps a user function as a typed predicate.
108
+ * Prefer `define<T>(...)` when creating a reusable custom guard from a runtime
109
+ * boolean check.
72
110
  *
73
111
  * Note: The correctness of the predicate is the caller's responsibility.
74
112
  * Its result is coerced to a boolean using `!!` for consistent guard behavior.
75
113
  *
76
114
  * @param fn Function that returns truthy when the value matches the target shape.
77
115
  * @returns Predicate narrowing to the intended type when it returns true.
116
+ * @example
117
+ * const isSlug = define<string>(
118
+ * (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
119
+ * );
78
120
  */
79
121
  declare function define<T>(fn: (value: unknown) => value is T): Predicate<T>;
80
122
  declare function define<T>(fn: (value: unknown) => boolean): Predicate<T>;
@@ -146,49 +188,76 @@ declare function lazy<T>(factory: () => Predicate<T>): Predicate<T>;
146
188
 
147
189
  /**
148
190
  * Combines a precondition guard with an additional refinement to narrow the type.
191
+ * Prefer this over hand-written `precondition(x) && condition(x)` when the
192
+ * composed predicate should be reused as a guard.
149
193
  *
150
194
  * @param precondition Broad guard evaluated first; short-circuits on failure.
151
195
  * @param condition Refinement evaluated only when `precondition` passes.
152
196
  * @returns Predicate that narrows the input to a subtype.
197
+ * @example
198
+ * const isPositiveNumber = and(
199
+ * isNumber,
200
+ * predicateToRefine<number>((value) => value > 0)
201
+ * );
202
+ * @see andAll
153
203
  */
154
204
  declare function and<A, B extends A>(precondition: Guard<A>, condition: Refine<A, B>): Predicate<B>;
155
205
  /**
156
206
  * Chains a sequence of refinements after a precondition, returning the final guard type.
207
+ * Use this instead of nested `&&` checks when each step should keep narrowing
208
+ * the next refinement.
157
209
  *
158
210
  * @param precondition Initial guard applied first.
159
211
  * @param steps Subsequent refinements applied in order.
160
212
  * @returns Guard reflecting the result of the refinement chain.
213
+ * @example
214
+ * const isPublicTitle = andAll(isString, minLength(4), startsWithPublic);
215
+ * @see and
161
216
  */
162
217
  declare function andAll<A>(precondition: Guard<A>): Guard<A>;
163
218
  declare function andAll<A, B extends A>(precondition: Guard<A>, step1: Refine<A, B>): Guard<B>;
164
219
  declare function andAll<A, B extends A, C extends B>(precondition: Guard<A>, step1: Refine<A, B>, step2: Refine<B, C>): Guard<C>;
165
220
  /**
166
221
  * Logical OR over multiple guards.
222
+ * Prefer this over hand-written `guardA(x) || guardB(x)` when the composed
223
+ * predicate should be reused as a guard.
167
224
  *
168
225
  * @param guards Guards to evaluate; passes if any guard passes.
169
226
  * @returns Guard for the union of all guarded types.
227
+ * @example
228
+ * const isId = or(isString, isNumber);
229
+ * @see oneOf
170
230
  */
171
231
  declare function or<P extends readonly Guard<unknown>[]>(...guards: P): Guard<OutOfGuards<P>>;
172
232
  /**
173
233
  * Adapts a guard so it can be used as a refinement within a known supertype.
174
234
  *
175
235
  * @returns Function that converts a `Guard<T>` into a `Refine<A, T>` when `T extends A`.
236
+ * @see and
176
237
  */
177
238
  declare function guardIn<A>(): <T extends A>(guard: Guard<T>) => Refine<A, T>;
178
239
  /**
179
240
  * Logical negation of a guard/refinement.
241
+ * Use this when a negated predicate should be named and reused.
180
242
  *
181
243
  * @param guard Guard or refinement to negate.
182
244
  * @returns Refinement excluding the guarded subtype from the input type.
245
+ * @example
246
+ * const isPresent = not(isNil);
183
247
  */
184
248
  declare function not<A, T>(guard: Guard<T>): Refine<A, Exclude<A, Extract<A, T>>>;
185
249
  declare function not<A, B extends A>(refine: Refine<A, B>): Refine<A, Exclude<A, B>>;
186
250
 
187
251
  /**
188
252
  * Allows `null` in addition to values accepted by the given guard/refinement.
253
+ * Use this instead of hand-writing `value === null || guard(value)` when the
254
+ * widened predicate should stay reusable.
189
255
  *
190
256
  * @param guard Guard/refinement for the base type.
191
257
  * @returns Guard/refinement widened to include `null`.
258
+ * @example
259
+ * const isNullableString = nullable(isString);
260
+ * @see nullish
192
261
  */
193
262
  declare function nullable<T>(guard: Guard<T>): Guard<T | null>;
194
263
  declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null, B | null>;
@@ -197,22 +266,33 @@ declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null
197
266
  *
198
267
  * @param guard Guard/refinement possibly including `null`.
199
268
  * @returns Guard/refinement with `null` excluded.
269
+ * @example
270
+ * const isNonNullString = nonNull(nullable(isString));
200
271
  */
201
272
  declare function nonNull<T>(guard: Guard<T>): Guard<Exclude<T, null>>;
202
273
  declare function nonNull<A, B extends A>(refine: Refine<A, B>): Refine<Exclude<A, null>, Exclude<B, null>>;
203
274
  /**
204
275
  * Allows `null` or `undefined` in addition to values accepted by the guard.
276
+ * Use this instead of hand-writing `isNil(value) || guard(value)` when widening
277
+ * another guard.
205
278
  *
206
279
  * @param guard Guard/refinement for the base type.
207
280
  * @returns Guard/refinement widened to include `null | undefined`.
281
+ * @example
282
+ * const isNullishString = nullish(isString);
283
+ * @see isNil
208
284
  */
209
285
  declare function nullish<T>(guard: Guard<T>): Guard<T | null | undefined>;
210
286
  declare function nullish<A, B extends A>(refine: Refine<A, B>): Refine<A | null | undefined, B | null | undefined>;
211
287
  /**
212
288
  * Allows `undefined` in addition to values accepted by the guard.
289
+ * Use this for value-level optionality. For optional object keys in `struct`,
290
+ * use `optionalKey(...)`.
213
291
  *
214
292
  * @param guard Guard/refinement for the base type.
215
293
  * @returns Guard/refinement widened to include `undefined`.
294
+ * @example
295
+ * const isOptionalString = optional(isString);
216
296
  */
217
297
  declare function optional<T>(guard: Guard<T>): Guard<T | undefined>;
218
298
  declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | undefined, B | undefined>;
@@ -221,6 +301,8 @@ declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | unde
221
301
  *
222
302
  * @param guard Guard/refinement that may accept `undefined`.
223
303
  * @returns Guard/refinement with `undefined` removed.
304
+ * @example
305
+ * const isRequiredString = required(optional(isString));
224
306
  */
225
307
  declare function required<T>(guard: Guard<T | undefined>): Guard<T>;
226
308
  declare function required<A, B extends A>(refine: Refine<A | undefined, B | undefined>): Refine<A, B>;
@@ -351,17 +433,21 @@ declare const isSymbol: Predicate<symbol>;
351
433
  /**
352
434
  * Checks whether a value is exactly `undefined`.
353
435
  *
354
- * To accept `null` as well, use {@link isNil}.
436
+ * For reusable nullish checks, use {@link isNil} or compose a named guard with
437
+ * {@link or} once and reuse it.
355
438
  *
356
439
  * @returns Predicate narrowing to `undefined`.
440
+ * @see isNil
357
441
  */
358
442
  declare const isUndefined: Predicate<undefined>;
359
443
  /**
360
444
  * Checks whether a value is exactly `null`.
361
445
  *
362
- * To accept `undefined` as well, use {@link isNil}.
446
+ * For reusable nullish checks, use {@link isNil} or compose a named guard with
447
+ * {@link or} once and reuse it.
363
448
  *
364
449
  * @returns Predicate narrowing to `null`.
450
+ * @see isNil
365
451
  */
366
452
  declare const isNull: Predicate<null>;
367
453
  /**
@@ -604,19 +690,6 @@ declare function struct<const S extends SchemaShape<S>>(schema: S, options?: {
604
690
  exact?: boolean;
605
691
  }): Predicate<InferSchema<S>>;
606
692
 
607
- type OptionalKeys<T> = {
608
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
609
- }[keyof T];
610
- type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
611
- type TypedStructShape<T extends object> = {
612
- readonly [K in Extract<RequiredKeys<T>, string>]-?: Predicate<T[K]>;
613
- } & {
614
- readonly [K in Extract<OptionalKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
615
- };
616
- type NoExtraKeys<S, Shape> = S & {
617
- readonly [K in Exclude<keyof S, keyof Shape>]: never;
618
- };
619
- type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
620
693
  /**
621
694
  * Creates a `struct` builder checked against an existing object type.
622
695
  * Optional target keys must also be declared with `optionalKey` so type drift
@@ -690,4 +763,4 @@ declare const everyOwnEnumerableEntry: (values: Record<string, unknown>, keyPred
690
763
  */
691
764
  declare const toBooleanPredicates: <A>(predicates: readonly ((value: A) => boolean)[]) => ReadonlyArray<(value: A) => boolean>;
692
765
 
693
- export { type ChainResult, type Guard, type GuardedOf, type GuardedWithin, type InferSchema, type OptionalSchemaField, type OutOfGuards, type ParseResult, type Predicate, type Primitive, type Refine, type RefineChain, type Refinement, type Schema, type SchemaField, and, andAll, arrayOf, assert, define, equals, equalsBy, equalsKey, everyArrayValue, everyMapEntry, everyOwnEnumerableEntry, everySetValue, everyTupleValue, guardIn, hasKey, hasKeys, isArray, isArrayBuffer, isAsyncIterable, isBigInt, isBlob, isBoolean, isDataView, isDate, isError, isFile, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNil, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, lazy, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeJsonParse, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
766
+ export { type ChainResult, type Guard, type GuardedOf, type GuardedWithin, type InferSchema, type NoExtraKeys, type OptionalObjectKeys, type OptionalSchemaField, type OutOfGuards, type ParseResult, type Predicate, type Primitive, type Refine, type RefineChain, type Refinement, type RequiredObjectKeys, type Schema, type SchemaField, type TypedStructFields, type TypedStructShape, and, andAll, arrayOf, assert, define, equals, equalsBy, equalsKey, everyArrayValue, everyMapEntry, everyOwnEnumerableEntry, everySetValue, everyTupleValue, guardIn, hasKey, hasKeys, isArray, isArrayBuffer, isAsyncIterable, isBigInt, isBlob, isBoolean, isDataView, isDate, isError, isFile, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNil, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, lazy, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeJsonParse, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * is-kit guard authoring guide:
3
+ * - Use define<T>(...) for reusable custom runtime checks.
4
+ * - Use and, andAll, or, and not to compose existing guards instead of
5
+ * hand-written boolean wrappers.
6
+ * - Use nullable, optional, or nullish to widen guards for nullish values.
7
+ * - Use struct or typedStruct for object-shape guards.
8
+ */
1
9
  type Predicate<T> = (value: unknown) => value is T;
2
10
  type Refinement<A, B extends A> = (value: A) => value is B;
3
11
  type Guard<T> = Predicate<T>;
@@ -55,6 +63,34 @@ type InferSchema<S extends SchemaShape<S>> = Simplify<{
55
63
  } & {
56
64
  readonly [K in OptionalSchemaKeys<S>]?: InferSchemaField<S[K]>;
57
65
  }>;
66
+ /**
67
+ * Extracts optional keys from an object type.
68
+ */
69
+ type OptionalObjectKeys<T> = {
70
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
71
+ }[keyof T];
72
+ /**
73
+ * Extracts required keys from an object type.
74
+ */
75
+ type RequiredObjectKeys<T> = Exclude<keyof T, OptionalObjectKeys<T>>;
76
+ /**
77
+ * Schema shape checked against an existing object type by `typedStruct`.
78
+ */
79
+ type TypedStructShape<T extends object> = {
80
+ readonly [K in Extract<RequiredObjectKeys<T>, string>]-?: Predicate<T[K]>;
81
+ } & {
82
+ readonly [K in Extract<OptionalObjectKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
83
+ };
84
+ /**
85
+ * Keeps a schema type while rejecting keys outside the expected shape.
86
+ */
87
+ type NoExtraKeys<S, Shape> = S & {
88
+ readonly [K in Exclude<keyof S, keyof Shape>]: never;
89
+ };
90
+ /**
91
+ * Field set accepted by `typedStruct` for a target object type.
92
+ */
93
+ type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
58
94
 
59
95
  /**
60
96
  * Asserts that a value satisfies a guard or refinement, otherwise throws.
@@ -69,12 +105,18 @@ declare function assert<A, B extends A>(refine: Refine<A, B>, value: A, message?
69
105
 
70
106
  /**
71
107
  * Wraps a user function as a typed predicate.
108
+ * Prefer `define<T>(...)` when creating a reusable custom guard from a runtime
109
+ * boolean check.
72
110
  *
73
111
  * Note: The correctness of the predicate is the caller's responsibility.
74
112
  * Its result is coerced to a boolean using `!!` for consistent guard behavior.
75
113
  *
76
114
  * @param fn Function that returns truthy when the value matches the target shape.
77
115
  * @returns Predicate narrowing to the intended type when it returns true.
116
+ * @example
117
+ * const isSlug = define<string>(
118
+ * (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
119
+ * );
78
120
  */
79
121
  declare function define<T>(fn: (value: unknown) => value is T): Predicate<T>;
80
122
  declare function define<T>(fn: (value: unknown) => boolean): Predicate<T>;
@@ -146,49 +188,76 @@ declare function lazy<T>(factory: () => Predicate<T>): Predicate<T>;
146
188
 
147
189
  /**
148
190
  * Combines a precondition guard with an additional refinement to narrow the type.
191
+ * Prefer this over hand-written `precondition(x) && condition(x)` when the
192
+ * composed predicate should be reused as a guard.
149
193
  *
150
194
  * @param precondition Broad guard evaluated first; short-circuits on failure.
151
195
  * @param condition Refinement evaluated only when `precondition` passes.
152
196
  * @returns Predicate that narrows the input to a subtype.
197
+ * @example
198
+ * const isPositiveNumber = and(
199
+ * isNumber,
200
+ * predicateToRefine<number>((value) => value > 0)
201
+ * );
202
+ * @see andAll
153
203
  */
154
204
  declare function and<A, B extends A>(precondition: Guard<A>, condition: Refine<A, B>): Predicate<B>;
155
205
  /**
156
206
  * Chains a sequence of refinements after a precondition, returning the final guard type.
207
+ * Use this instead of nested `&&` checks when each step should keep narrowing
208
+ * the next refinement.
157
209
  *
158
210
  * @param precondition Initial guard applied first.
159
211
  * @param steps Subsequent refinements applied in order.
160
212
  * @returns Guard reflecting the result of the refinement chain.
213
+ * @example
214
+ * const isPublicTitle = andAll(isString, minLength(4), startsWithPublic);
215
+ * @see and
161
216
  */
162
217
  declare function andAll<A>(precondition: Guard<A>): Guard<A>;
163
218
  declare function andAll<A, B extends A>(precondition: Guard<A>, step1: Refine<A, B>): Guard<B>;
164
219
  declare function andAll<A, B extends A, C extends B>(precondition: Guard<A>, step1: Refine<A, B>, step2: Refine<B, C>): Guard<C>;
165
220
  /**
166
221
  * Logical OR over multiple guards.
222
+ * Prefer this over hand-written `guardA(x) || guardB(x)` when the composed
223
+ * predicate should be reused as a guard.
167
224
  *
168
225
  * @param guards Guards to evaluate; passes if any guard passes.
169
226
  * @returns Guard for the union of all guarded types.
227
+ * @example
228
+ * const isId = or(isString, isNumber);
229
+ * @see oneOf
170
230
  */
171
231
  declare function or<P extends readonly Guard<unknown>[]>(...guards: P): Guard<OutOfGuards<P>>;
172
232
  /**
173
233
  * Adapts a guard so it can be used as a refinement within a known supertype.
174
234
  *
175
235
  * @returns Function that converts a `Guard<T>` into a `Refine<A, T>` when `T extends A`.
236
+ * @see and
176
237
  */
177
238
  declare function guardIn<A>(): <T extends A>(guard: Guard<T>) => Refine<A, T>;
178
239
  /**
179
240
  * Logical negation of a guard/refinement.
241
+ * Use this when a negated predicate should be named and reused.
180
242
  *
181
243
  * @param guard Guard or refinement to negate.
182
244
  * @returns Refinement excluding the guarded subtype from the input type.
245
+ * @example
246
+ * const isPresent = not(isNil);
183
247
  */
184
248
  declare function not<A, T>(guard: Guard<T>): Refine<A, Exclude<A, Extract<A, T>>>;
185
249
  declare function not<A, B extends A>(refine: Refine<A, B>): Refine<A, Exclude<A, B>>;
186
250
 
187
251
  /**
188
252
  * Allows `null` in addition to values accepted by the given guard/refinement.
253
+ * Use this instead of hand-writing `value === null || guard(value)` when the
254
+ * widened predicate should stay reusable.
189
255
  *
190
256
  * @param guard Guard/refinement for the base type.
191
257
  * @returns Guard/refinement widened to include `null`.
258
+ * @example
259
+ * const isNullableString = nullable(isString);
260
+ * @see nullish
192
261
  */
193
262
  declare function nullable<T>(guard: Guard<T>): Guard<T | null>;
194
263
  declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null, B | null>;
@@ -197,22 +266,33 @@ declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null
197
266
  *
198
267
  * @param guard Guard/refinement possibly including `null`.
199
268
  * @returns Guard/refinement with `null` excluded.
269
+ * @example
270
+ * const isNonNullString = nonNull(nullable(isString));
200
271
  */
201
272
  declare function nonNull<T>(guard: Guard<T>): Guard<Exclude<T, null>>;
202
273
  declare function nonNull<A, B extends A>(refine: Refine<A, B>): Refine<Exclude<A, null>, Exclude<B, null>>;
203
274
  /**
204
275
  * Allows `null` or `undefined` in addition to values accepted by the guard.
276
+ * Use this instead of hand-writing `isNil(value) || guard(value)` when widening
277
+ * another guard.
205
278
  *
206
279
  * @param guard Guard/refinement for the base type.
207
280
  * @returns Guard/refinement widened to include `null | undefined`.
281
+ * @example
282
+ * const isNullishString = nullish(isString);
283
+ * @see isNil
208
284
  */
209
285
  declare function nullish<T>(guard: Guard<T>): Guard<T | null | undefined>;
210
286
  declare function nullish<A, B extends A>(refine: Refine<A, B>): Refine<A | null | undefined, B | null | undefined>;
211
287
  /**
212
288
  * Allows `undefined` in addition to values accepted by the guard.
289
+ * Use this for value-level optionality. For optional object keys in `struct`,
290
+ * use `optionalKey(...)`.
213
291
  *
214
292
  * @param guard Guard/refinement for the base type.
215
293
  * @returns Guard/refinement widened to include `undefined`.
294
+ * @example
295
+ * const isOptionalString = optional(isString);
216
296
  */
217
297
  declare function optional<T>(guard: Guard<T>): Guard<T | undefined>;
218
298
  declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | undefined, B | undefined>;
@@ -221,6 +301,8 @@ declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | unde
221
301
  *
222
302
  * @param guard Guard/refinement that may accept `undefined`.
223
303
  * @returns Guard/refinement with `undefined` removed.
304
+ * @example
305
+ * const isRequiredString = required(optional(isString));
224
306
  */
225
307
  declare function required<T>(guard: Guard<T | undefined>): Guard<T>;
226
308
  declare function required<A, B extends A>(refine: Refine<A | undefined, B | undefined>): Refine<A, B>;
@@ -351,17 +433,21 @@ declare const isSymbol: Predicate<symbol>;
351
433
  /**
352
434
  * Checks whether a value is exactly `undefined`.
353
435
  *
354
- * To accept `null` as well, use {@link isNil}.
436
+ * For reusable nullish checks, use {@link isNil} or compose a named guard with
437
+ * {@link or} once and reuse it.
355
438
  *
356
439
  * @returns Predicate narrowing to `undefined`.
440
+ * @see isNil
357
441
  */
358
442
  declare const isUndefined: Predicate<undefined>;
359
443
  /**
360
444
  * Checks whether a value is exactly `null`.
361
445
  *
362
- * To accept `undefined` as well, use {@link isNil}.
446
+ * For reusable nullish checks, use {@link isNil} or compose a named guard with
447
+ * {@link or} once and reuse it.
363
448
  *
364
449
  * @returns Predicate narrowing to `null`.
450
+ * @see isNil
365
451
  */
366
452
  declare const isNull: Predicate<null>;
367
453
  /**
@@ -604,19 +690,6 @@ declare function struct<const S extends SchemaShape<S>>(schema: S, options?: {
604
690
  exact?: boolean;
605
691
  }): Predicate<InferSchema<S>>;
606
692
 
607
- type OptionalKeys<T> = {
608
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
609
- }[keyof T];
610
- type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
611
- type TypedStructShape<T extends object> = {
612
- readonly [K in Extract<RequiredKeys<T>, string>]-?: Predicate<T[K]>;
613
- } & {
614
- readonly [K in Extract<OptionalKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
615
- };
616
- type NoExtraKeys<S, Shape> = S & {
617
- readonly [K in Exclude<keyof S, keyof Shape>]: never;
618
- };
619
- type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
620
693
  /**
621
694
  * Creates a `struct` builder checked against an existing object type.
622
695
  * Optional target keys must also be declared with `optionalKey` so type drift
@@ -690,4 +763,4 @@ declare const everyOwnEnumerableEntry: (values: Record<string, unknown>, keyPred
690
763
  */
691
764
  declare const toBooleanPredicates: <A>(predicates: readonly ((value: A) => boolean)[]) => ReadonlyArray<(value: A) => boolean>;
692
765
 
693
- export { type ChainResult, type Guard, type GuardedOf, type GuardedWithin, type InferSchema, type OptionalSchemaField, type OutOfGuards, type ParseResult, type Predicate, type Primitive, type Refine, type RefineChain, type Refinement, type Schema, type SchemaField, and, andAll, arrayOf, assert, define, equals, equalsBy, equalsKey, everyArrayValue, everyMapEntry, everyOwnEnumerableEntry, everySetValue, everyTupleValue, guardIn, hasKey, hasKeys, isArray, isArrayBuffer, isAsyncIterable, isBigInt, isBlob, isBoolean, isDataView, isDate, isError, isFile, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNil, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, lazy, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeJsonParse, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
766
+ export { type ChainResult, type Guard, type GuardedOf, type GuardedWithin, type InferSchema, type NoExtraKeys, type OptionalObjectKeys, type OptionalSchemaField, type OutOfGuards, type ParseResult, type Predicate, type Primitive, type Refine, type RefineChain, type Refinement, type RequiredObjectKeys, type Schema, type SchemaField, type TypedStructFields, type TypedStructShape, and, andAll, arrayOf, assert, define, equals, equalsBy, equalsKey, everyArrayValue, everyMapEntry, everyOwnEnumerableEntry, everySetValue, everyTupleValue, guardIn, hasKey, hasKeys, isArray, isArrayBuffer, isAsyncIterable, isBigInt, isBlob, isBoolean, isDataView, isDate, isError, isFile, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNil, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, lazy, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeJsonParse, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
package/dist/index.js CHANGED
@@ -114,6 +114,10 @@ function define(fn) {
114
114
  return (value) => !!fn(value);
115
115
  }
116
116
 
117
+ // src/utils/own-properties.ts
118
+ var hasOwnPropertyKey = (value, key) => Object.hasOwn(value, key);
119
+ var hasOwnPropertyKeys = (value, keys) => keys.every((key) => hasOwnPropertyKey(value, key));
120
+
117
121
  // src/utils/guard-collections.ts
118
122
  var everyIterableValue = (values, predicate) => {
119
123
  for (const value of values) {
@@ -237,19 +241,19 @@ function equalsBy(guard, selector) {
237
241
  }
238
242
  function equalsKey(key, target) {
239
243
  const hasMatchingKey = define((input) => {
240
- return isObject(input) && Object.hasOwn(input, key) && Object.is(input[key], target);
244
+ return isObject(input) && hasOwnPropertyKey(input, key) && Object.is(input[key], target);
241
245
  });
242
246
  return (input) => hasMatchingKey(input);
243
247
  }
244
248
 
245
249
  // src/core/key.ts
246
- var hasKey = (key) => define((input) => isObject(input) && Object.hasOwn(input, key));
250
+ var hasKey = (key) => define((input) => isObject(input) && hasOwnPropertyKey(input, key));
247
251
  var hasKeys = (...keys) => {
248
252
  if (keys.length === 0) {
249
253
  return define(() => false);
250
254
  }
251
255
  return define(
252
- (input) => isObject(input) && keys.every((key) => Object.hasOwn(input, key))
256
+ (input) => isObject(input) && hasOwnPropertyKeys(input, keys)
253
257
  );
254
258
  };
255
259
  function narrowKeyTo(guard, key) {
@@ -469,12 +473,14 @@ var isOptionalSchemaField = define(
469
473
  var hasRequiredKeys = (obj, entries) => (
470
474
  // WHY: Required fields must be own properties; inherited values should not
471
475
  // satisfy a schema because `struct` models object payload shape, not prototype chains.
472
- entries.every(([key, guard]) => Object.hasOwn(obj, key) && guard(obj[key]))
476
+ entries.every(([key, guard]) => hasOwnPropertyKey(obj, key) && guard(obj[key]))
473
477
  );
474
478
  var hasValidOptionalKeys = (obj, entries) => (
475
479
  // WHY: Optional keys are validated only when present. Missing keys stay valid
476
480
  // without forcing callers to encode `undefined` into the value guard.
477
- entries.every(([key, guard]) => !Object.hasOwn(obj, key) || guard(obj[key]))
481
+ entries.every(
482
+ ([key, guard]) => !hasOwnPropertyKey(obj, key) || guard(obj[key])
483
+ )
478
484
  );
479
485
  var hasOnlyAllowedKeys = (obj, allowed) => Object.keys(obj).every((key) => allowed.has(key));
480
486
  function optionalKey(guard) {
@@ -485,7 +491,7 @@ function struct(schema, options) {
485
491
  const optionalEntries = [];
486
492
  const schemaKeys = [];
487
493
  for (const key in schema) {
488
- if (!Object.hasOwn(schema, key)) continue;
494
+ if (!hasOwnPropertyKey(schema, key)) continue;
489
495
  const field = schema[key];
490
496
  schemaKeys.push(key);
491
497
  if (isOptionalSchemaField(field)) {
package/dist/index.mjs CHANGED
@@ -10,6 +10,10 @@ function define(fn) {
10
10
  return (value) => !!fn(value);
11
11
  }
12
12
 
13
+ // src/utils/own-properties.ts
14
+ var hasOwnPropertyKey = (value, key) => Object.hasOwn(value, key);
15
+ var hasOwnPropertyKeys = (value, keys) => keys.every((key) => hasOwnPropertyKey(value, key));
16
+
13
17
  // src/utils/guard-collections.ts
14
18
  var everyIterableValue = (values, predicate) => {
15
19
  for (const value of values) {
@@ -133,19 +137,19 @@ function equalsBy(guard, selector) {
133
137
  }
134
138
  function equalsKey(key, target) {
135
139
  const hasMatchingKey = define((input) => {
136
- return isObject(input) && Object.hasOwn(input, key) && Object.is(input[key], target);
140
+ return isObject(input) && hasOwnPropertyKey(input, key) && Object.is(input[key], target);
137
141
  });
138
142
  return (input) => hasMatchingKey(input);
139
143
  }
140
144
 
141
145
  // src/core/key.ts
142
- var hasKey = (key) => define((input) => isObject(input) && Object.hasOwn(input, key));
146
+ var hasKey = (key) => define((input) => isObject(input) && hasOwnPropertyKey(input, key));
143
147
  var hasKeys = (...keys) => {
144
148
  if (keys.length === 0) {
145
149
  return define(() => false);
146
150
  }
147
151
  return define(
148
- (input) => isObject(input) && keys.every((key) => Object.hasOwn(input, key))
152
+ (input) => isObject(input) && hasOwnPropertyKeys(input, keys)
149
153
  );
150
154
  };
151
155
  function narrowKeyTo(guard, key) {
@@ -365,12 +369,14 @@ var isOptionalSchemaField = define(
365
369
  var hasRequiredKeys = (obj, entries) => (
366
370
  // WHY: Required fields must be own properties; inherited values should not
367
371
  // satisfy a schema because `struct` models object payload shape, not prototype chains.
368
- entries.every(([key, guard]) => Object.hasOwn(obj, key) && guard(obj[key]))
372
+ entries.every(([key, guard]) => hasOwnPropertyKey(obj, key) && guard(obj[key]))
369
373
  );
370
374
  var hasValidOptionalKeys = (obj, entries) => (
371
375
  // WHY: Optional keys are validated only when present. Missing keys stay valid
372
376
  // without forcing callers to encode `undefined` into the value guard.
373
- entries.every(([key, guard]) => !Object.hasOwn(obj, key) || guard(obj[key]))
377
+ entries.every(
378
+ ([key, guard]) => !hasOwnPropertyKey(obj, key) || guard(obj[key])
379
+ )
374
380
  );
375
381
  var hasOnlyAllowedKeys = (obj, allowed) => Object.keys(obj).every((key) => allowed.has(key));
376
382
  function optionalKey(guard) {
@@ -381,7 +387,7 @@ function struct(schema, options) {
381
387
  const optionalEntries = [];
382
388
  const schemaKeys = [];
383
389
  for (const key in schema) {
384
- if (!Object.hasOwn(schema, key)) continue;
390
+ if (!hasOwnPropertyKey(schema, key)) continue;
385
391
  const field = schema[key];
386
392
  schemaKeys.push(key);
387
393
  if (isOptionalSchemaField(field)) {
package/docs/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # is-kit docs
2
+
3
+ This directory contains the public documentation site for `is-kit`.
4
+
5
+ The site is built with Next.js and shadcn/ui. It serves the marketing pages, hand-written API guides, and the generated TypeDoc output.
6
+
7
+ ## What lives here
8
+
9
+ - `app/` - Next.js routes and page entry points
10
+ - `components/` - reusable docs-site UI and page sections
11
+ - `constants/` - site navigation and page metadata
12
+ - `lib/` - docs-specific helpers and data shaping
13
+ - `public/` - static assets and generated API output
14
+
15
+ ## Local development
16
+
17
+ From the repository root:
18
+
19
+ ```bash
20
+ pnpm --filter ./docs dev
21
+ ```
22
+
23
+ Other common commands:
24
+
25
+ ```bash
26
+ pnpm --filter ./docs build
27
+ pnpm --filter ./docs start
28
+ ```
29
+
30
+ If you use `mise`, run the same commands through `mise exec`.
31
+
32
+ ## Generated API reference
33
+
34
+ The API reference is generated from the library source with TypeDoc and written to `docs/public/api`.
35
+
36
+ From the repository root:
37
+
38
+ ```bash
39
+ pnpm typedoc
40
+ ```
41
+
42
+ After generation, the docs site serves that static output under `/api`.
43
+
44
+ ## Common editing workflow
45
+
46
+ ### Update a guide or landing page
47
+
48
+ Edit the relevant route in `app/` and the supporting components in `components/`.
49
+
50
+ ### Update navigation or docs metadata
51
+
52
+ Check the files under `constants/` and `lib/`.
53
+
54
+ ### Refresh generated API docs
55
+
56
+ Run:
57
+
58
+ ```bash
59
+ pnpm typedoc
60
+ ```
61
+
62
+ ### Add or update static assets
63
+
64
+ Place them under `public/`.
65
+
66
+ ## shadcn/ui
67
+
68
+ The docs app is already configured for shadcn/ui with the `@/*` alias.
69
+
70
+ List available components:
71
+
72
+ ```bash
73
+ pnpm --filter ./docs run shadcn:list
74
+ ```
75
+
76
+ Add a component:
77
+
78
+ ```bash
79
+ pnpm --filter ./docs run shadcn:add button
80
+ ```
81
+
82
+ If you need to reinitialize shadcn:
83
+
84
+ ```bash
85
+ pnpm --filter ./docs run shadcn:init
86
+ ```
87
+
88
+ ## Example
89
+
90
+ ```tsx
91
+ import { Button } from '@/components/ui/button';
92
+
93
+ export default function Example() {
94
+ return (
95
+ <div className='space-x-2'>
96
+ <Button>Default</Button>
97
+ <Button variant='secondary'>Secondary</Button>
98
+ </div>
99
+ );
100
+ }
101
+ ```
@@ -0,0 +1,63 @@
1
+ # is-kit Agent Rules
2
+
3
+ Copy this into a consumer repository's `AGENTS.md`, `CLAUDE.md`, or
4
+ `.agents/rules/is-kit.md` when AI agents generate or review code that uses
5
+ `is-kit`.
6
+
7
+ ## Usage Rules
8
+
9
+ - Build reusable custom guards with `define<T>(...)` instead of writing
10
+ standalone type-predicate functions by hand.
11
+ - Prefer `is-kit` combinators over hand-written boolean composition when
12
+ combining existing guards.
13
+ - Use `or(isA, isB)` instead of `(value) => isA(value) || isB(value)` when the
14
+ result should be a reusable guard.
15
+ - Use `and(baseGuard, refinement)` or `andAll(...)` instead of hand-written
16
+ `&&` checks when narrowing should be preserved.
17
+ - Use `not(guard)` for reusable negated guards.
18
+ - Use `nullable(guard)`, `optional(guard)`, or `nullish(guard)` when widening an
19
+ existing guard to accept `null` and/or `undefined`.
20
+ - Use `isNil(value)` when checking a value directly for `null | undefined`.
21
+ - Use `optionalKey(guard)` for optional object keys inside `struct`.
22
+
23
+ ## Preferred Examples
24
+
25
+ ```ts
26
+ import {
27
+ and,
28
+ define,
29
+ isNil,
30
+ isNumber,
31
+ isString,
32
+ nullish,
33
+ or,
34
+ predicateToRefine
35
+ } from 'is-kit';
36
+
37
+ const isId = or(isString, isNumber);
38
+ const isMaybeName = nullish(isString);
39
+ const isSlug = define<string>(
40
+ (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
41
+ );
42
+ const isPositiveNumber = and(
43
+ isNumber,
44
+ predicateToRefine<number>((value) => value > 0)
45
+ );
46
+
47
+ declare const value: unknown;
48
+
49
+ if (isNil(value)) {
50
+ return;
51
+ }
52
+ ```
53
+
54
+ ## Avoid
55
+
56
+ ```ts
57
+ declare const value: unknown;
58
+
59
+ const isId = (value: unknown) => isString(value) || isNumber(value);
60
+ const isMaybeName = (value: unknown) => value == null || isString(value);
61
+ const isSlug = (value: unknown): value is string =>
62
+ isString(value) && /^[a-z0-9-]+$/.test(value);
63
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "is-kit",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Make 'isXXX' easier. Let's make your code type safe and more readable!",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "files": [
24
24
  "dist",
25
+ "docs/agent-rules.md",
25
26
  "README.md",
26
27
  "LICENSE"
27
28
  ],