is-kit 1.11.0 → 1.11.1
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 +63 -0
- package/dist/index.d.mts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +12 -6
- package/dist/index.mjs +12 -6
- package/docs/README.md +101 -0
- package/docs/agent-rules.md +63 -0
- package/package.json +2 -1
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>;
|
|
@@ -69,12 +77,18 @@ declare function assert<A, B extends A>(refine: Refine<A, B>, value: A, message?
|
|
|
69
77
|
|
|
70
78
|
/**
|
|
71
79
|
* Wraps a user function as a typed predicate.
|
|
80
|
+
* Prefer `define<T>(...)` when creating a reusable custom guard from a runtime
|
|
81
|
+
* boolean check.
|
|
72
82
|
*
|
|
73
83
|
* Note: The correctness of the predicate is the caller's responsibility.
|
|
74
84
|
* Its result is coerced to a boolean using `!!` for consistent guard behavior.
|
|
75
85
|
*
|
|
76
86
|
* @param fn Function that returns truthy when the value matches the target shape.
|
|
77
87
|
* @returns Predicate narrowing to the intended type when it returns true.
|
|
88
|
+
* @example
|
|
89
|
+
* const isSlug = define<string>(
|
|
90
|
+
* (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
|
|
91
|
+
* );
|
|
78
92
|
*/
|
|
79
93
|
declare function define<T>(fn: (value: unknown) => value is T): Predicate<T>;
|
|
80
94
|
declare function define<T>(fn: (value: unknown) => boolean): Predicate<T>;
|
|
@@ -146,49 +160,76 @@ declare function lazy<T>(factory: () => Predicate<T>): Predicate<T>;
|
|
|
146
160
|
|
|
147
161
|
/**
|
|
148
162
|
* Combines a precondition guard with an additional refinement to narrow the type.
|
|
163
|
+
* Prefer this over hand-written `precondition(x) && condition(x)` when the
|
|
164
|
+
* composed predicate should be reused as a guard.
|
|
149
165
|
*
|
|
150
166
|
* @param precondition Broad guard evaluated first; short-circuits on failure.
|
|
151
167
|
* @param condition Refinement evaluated only when `precondition` passes.
|
|
152
168
|
* @returns Predicate that narrows the input to a subtype.
|
|
169
|
+
* @example
|
|
170
|
+
* const isPositiveNumber = and(
|
|
171
|
+
* isNumber,
|
|
172
|
+
* predicateToRefine<number>((value) => value > 0)
|
|
173
|
+
* );
|
|
174
|
+
* @see andAll
|
|
153
175
|
*/
|
|
154
176
|
declare function and<A, B extends A>(precondition: Guard<A>, condition: Refine<A, B>): Predicate<B>;
|
|
155
177
|
/**
|
|
156
178
|
* Chains a sequence of refinements after a precondition, returning the final guard type.
|
|
179
|
+
* Use this instead of nested `&&` checks when each step should keep narrowing
|
|
180
|
+
* the next refinement.
|
|
157
181
|
*
|
|
158
182
|
* @param precondition Initial guard applied first.
|
|
159
183
|
* @param steps Subsequent refinements applied in order.
|
|
160
184
|
* @returns Guard reflecting the result of the refinement chain.
|
|
185
|
+
* @example
|
|
186
|
+
* const isPublicTitle = andAll(isString, minLength(4), startsWithPublic);
|
|
187
|
+
* @see and
|
|
161
188
|
*/
|
|
162
189
|
declare function andAll<A>(precondition: Guard<A>): Guard<A>;
|
|
163
190
|
declare function andAll<A, B extends A>(precondition: Guard<A>, step1: Refine<A, B>): Guard<B>;
|
|
164
191
|
declare function andAll<A, B extends A, C extends B>(precondition: Guard<A>, step1: Refine<A, B>, step2: Refine<B, C>): Guard<C>;
|
|
165
192
|
/**
|
|
166
193
|
* Logical OR over multiple guards.
|
|
194
|
+
* Prefer this over hand-written `guardA(x) || guardB(x)` when the composed
|
|
195
|
+
* predicate should be reused as a guard.
|
|
167
196
|
*
|
|
168
197
|
* @param guards Guards to evaluate; passes if any guard passes.
|
|
169
198
|
* @returns Guard for the union of all guarded types.
|
|
199
|
+
* @example
|
|
200
|
+
* const isId = or(isString, isNumber);
|
|
201
|
+
* @see oneOf
|
|
170
202
|
*/
|
|
171
203
|
declare function or<P extends readonly Guard<unknown>[]>(...guards: P): Guard<OutOfGuards<P>>;
|
|
172
204
|
/**
|
|
173
205
|
* Adapts a guard so it can be used as a refinement within a known supertype.
|
|
174
206
|
*
|
|
175
207
|
* @returns Function that converts a `Guard<T>` into a `Refine<A, T>` when `T extends A`.
|
|
208
|
+
* @see and
|
|
176
209
|
*/
|
|
177
210
|
declare function guardIn<A>(): <T extends A>(guard: Guard<T>) => Refine<A, T>;
|
|
178
211
|
/**
|
|
179
212
|
* Logical negation of a guard/refinement.
|
|
213
|
+
* Use this when a negated predicate should be named and reused.
|
|
180
214
|
*
|
|
181
215
|
* @param guard Guard or refinement to negate.
|
|
182
216
|
* @returns Refinement excluding the guarded subtype from the input type.
|
|
217
|
+
* @example
|
|
218
|
+
* const isPresent = not(isNil);
|
|
183
219
|
*/
|
|
184
220
|
declare function not<A, T>(guard: Guard<T>): Refine<A, Exclude<A, Extract<A, T>>>;
|
|
185
221
|
declare function not<A, B extends A>(refine: Refine<A, B>): Refine<A, Exclude<A, B>>;
|
|
186
222
|
|
|
187
223
|
/**
|
|
188
224
|
* Allows `null` in addition to values accepted by the given guard/refinement.
|
|
225
|
+
* Use this instead of hand-writing `value === null || guard(value)` when the
|
|
226
|
+
* widened predicate should stay reusable.
|
|
189
227
|
*
|
|
190
228
|
* @param guard Guard/refinement for the base type.
|
|
191
229
|
* @returns Guard/refinement widened to include `null`.
|
|
230
|
+
* @example
|
|
231
|
+
* const isNullableString = nullable(isString);
|
|
232
|
+
* @see nullish
|
|
192
233
|
*/
|
|
193
234
|
declare function nullable<T>(guard: Guard<T>): Guard<T | null>;
|
|
194
235
|
declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null, B | null>;
|
|
@@ -197,22 +238,33 @@ declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null
|
|
|
197
238
|
*
|
|
198
239
|
* @param guard Guard/refinement possibly including `null`.
|
|
199
240
|
* @returns Guard/refinement with `null` excluded.
|
|
241
|
+
* @example
|
|
242
|
+
* const isNonNullString = nonNull(nullable(isString));
|
|
200
243
|
*/
|
|
201
244
|
declare function nonNull<T>(guard: Guard<T>): Guard<Exclude<T, null>>;
|
|
202
245
|
declare function nonNull<A, B extends A>(refine: Refine<A, B>): Refine<Exclude<A, null>, Exclude<B, null>>;
|
|
203
246
|
/**
|
|
204
247
|
* Allows `null` or `undefined` in addition to values accepted by the guard.
|
|
248
|
+
* Use this instead of hand-writing `isNil(value) || guard(value)` when widening
|
|
249
|
+
* another guard.
|
|
205
250
|
*
|
|
206
251
|
* @param guard Guard/refinement for the base type.
|
|
207
252
|
* @returns Guard/refinement widened to include `null | undefined`.
|
|
253
|
+
* @example
|
|
254
|
+
* const isNullishString = nullish(isString);
|
|
255
|
+
* @see isNil
|
|
208
256
|
*/
|
|
209
257
|
declare function nullish<T>(guard: Guard<T>): Guard<T | null | undefined>;
|
|
210
258
|
declare function nullish<A, B extends A>(refine: Refine<A, B>): Refine<A | null | undefined, B | null | undefined>;
|
|
211
259
|
/**
|
|
212
260
|
* Allows `undefined` in addition to values accepted by the guard.
|
|
261
|
+
* Use this for value-level optionality. For optional object keys in `struct`,
|
|
262
|
+
* use `optionalKey(...)`.
|
|
213
263
|
*
|
|
214
264
|
* @param guard Guard/refinement for the base type.
|
|
215
265
|
* @returns Guard/refinement widened to include `undefined`.
|
|
266
|
+
* @example
|
|
267
|
+
* const isOptionalString = optional(isString);
|
|
216
268
|
*/
|
|
217
269
|
declare function optional<T>(guard: Guard<T>): Guard<T | undefined>;
|
|
218
270
|
declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | undefined, B | undefined>;
|
|
@@ -221,6 +273,8 @@ declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | unde
|
|
|
221
273
|
*
|
|
222
274
|
* @param guard Guard/refinement that may accept `undefined`.
|
|
223
275
|
* @returns Guard/refinement with `undefined` removed.
|
|
276
|
+
* @example
|
|
277
|
+
* const isRequiredString = required(optional(isString));
|
|
224
278
|
*/
|
|
225
279
|
declare function required<T>(guard: Guard<T | undefined>): Guard<T>;
|
|
226
280
|
declare function required<A, B extends A>(refine: Refine<A | undefined, B | undefined>): Refine<A, B>;
|
|
@@ -351,17 +405,21 @@ declare const isSymbol: Predicate<symbol>;
|
|
|
351
405
|
/**
|
|
352
406
|
* Checks whether a value is exactly `undefined`.
|
|
353
407
|
*
|
|
354
|
-
*
|
|
408
|
+
* For reusable nullish checks, use {@link isNil} or compose a named guard with
|
|
409
|
+
* {@link or} once and reuse it.
|
|
355
410
|
*
|
|
356
411
|
* @returns Predicate narrowing to `undefined`.
|
|
412
|
+
* @see isNil
|
|
357
413
|
*/
|
|
358
414
|
declare const isUndefined: Predicate<undefined>;
|
|
359
415
|
/**
|
|
360
416
|
* Checks whether a value is exactly `null`.
|
|
361
417
|
*
|
|
362
|
-
*
|
|
418
|
+
* For reusable nullish checks, use {@link isNil} or compose a named guard with
|
|
419
|
+
* {@link or} once and reuse it.
|
|
363
420
|
*
|
|
364
421
|
* @returns Predicate narrowing to `null`.
|
|
422
|
+
* @see isNil
|
|
365
423
|
*/
|
|
366
424
|
declare const isNull: Predicate<null>;
|
|
367
425
|
/**
|
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>;
|
|
@@ -69,12 +77,18 @@ declare function assert<A, B extends A>(refine: Refine<A, B>, value: A, message?
|
|
|
69
77
|
|
|
70
78
|
/**
|
|
71
79
|
* Wraps a user function as a typed predicate.
|
|
80
|
+
* Prefer `define<T>(...)` when creating a reusable custom guard from a runtime
|
|
81
|
+
* boolean check.
|
|
72
82
|
*
|
|
73
83
|
* Note: The correctness of the predicate is the caller's responsibility.
|
|
74
84
|
* Its result is coerced to a boolean using `!!` for consistent guard behavior.
|
|
75
85
|
*
|
|
76
86
|
* @param fn Function that returns truthy when the value matches the target shape.
|
|
77
87
|
* @returns Predicate narrowing to the intended type when it returns true.
|
|
88
|
+
* @example
|
|
89
|
+
* const isSlug = define<string>(
|
|
90
|
+
* (value) => isString(value) && /^[a-z0-9-]+$/.test(value)
|
|
91
|
+
* );
|
|
78
92
|
*/
|
|
79
93
|
declare function define<T>(fn: (value: unknown) => value is T): Predicate<T>;
|
|
80
94
|
declare function define<T>(fn: (value: unknown) => boolean): Predicate<T>;
|
|
@@ -146,49 +160,76 @@ declare function lazy<T>(factory: () => Predicate<T>): Predicate<T>;
|
|
|
146
160
|
|
|
147
161
|
/**
|
|
148
162
|
* Combines a precondition guard with an additional refinement to narrow the type.
|
|
163
|
+
* Prefer this over hand-written `precondition(x) && condition(x)` when the
|
|
164
|
+
* composed predicate should be reused as a guard.
|
|
149
165
|
*
|
|
150
166
|
* @param precondition Broad guard evaluated first; short-circuits on failure.
|
|
151
167
|
* @param condition Refinement evaluated only when `precondition` passes.
|
|
152
168
|
* @returns Predicate that narrows the input to a subtype.
|
|
169
|
+
* @example
|
|
170
|
+
* const isPositiveNumber = and(
|
|
171
|
+
* isNumber,
|
|
172
|
+
* predicateToRefine<number>((value) => value > 0)
|
|
173
|
+
* );
|
|
174
|
+
* @see andAll
|
|
153
175
|
*/
|
|
154
176
|
declare function and<A, B extends A>(precondition: Guard<A>, condition: Refine<A, B>): Predicate<B>;
|
|
155
177
|
/**
|
|
156
178
|
* Chains a sequence of refinements after a precondition, returning the final guard type.
|
|
179
|
+
* Use this instead of nested `&&` checks when each step should keep narrowing
|
|
180
|
+
* the next refinement.
|
|
157
181
|
*
|
|
158
182
|
* @param precondition Initial guard applied first.
|
|
159
183
|
* @param steps Subsequent refinements applied in order.
|
|
160
184
|
* @returns Guard reflecting the result of the refinement chain.
|
|
185
|
+
* @example
|
|
186
|
+
* const isPublicTitle = andAll(isString, minLength(4), startsWithPublic);
|
|
187
|
+
* @see and
|
|
161
188
|
*/
|
|
162
189
|
declare function andAll<A>(precondition: Guard<A>): Guard<A>;
|
|
163
190
|
declare function andAll<A, B extends A>(precondition: Guard<A>, step1: Refine<A, B>): Guard<B>;
|
|
164
191
|
declare function andAll<A, B extends A, C extends B>(precondition: Guard<A>, step1: Refine<A, B>, step2: Refine<B, C>): Guard<C>;
|
|
165
192
|
/**
|
|
166
193
|
* Logical OR over multiple guards.
|
|
194
|
+
* Prefer this over hand-written `guardA(x) || guardB(x)` when the composed
|
|
195
|
+
* predicate should be reused as a guard.
|
|
167
196
|
*
|
|
168
197
|
* @param guards Guards to evaluate; passes if any guard passes.
|
|
169
198
|
* @returns Guard for the union of all guarded types.
|
|
199
|
+
* @example
|
|
200
|
+
* const isId = or(isString, isNumber);
|
|
201
|
+
* @see oneOf
|
|
170
202
|
*/
|
|
171
203
|
declare function or<P extends readonly Guard<unknown>[]>(...guards: P): Guard<OutOfGuards<P>>;
|
|
172
204
|
/**
|
|
173
205
|
* Adapts a guard so it can be used as a refinement within a known supertype.
|
|
174
206
|
*
|
|
175
207
|
* @returns Function that converts a `Guard<T>` into a `Refine<A, T>` when `T extends A`.
|
|
208
|
+
* @see and
|
|
176
209
|
*/
|
|
177
210
|
declare function guardIn<A>(): <T extends A>(guard: Guard<T>) => Refine<A, T>;
|
|
178
211
|
/**
|
|
179
212
|
* Logical negation of a guard/refinement.
|
|
213
|
+
* Use this when a negated predicate should be named and reused.
|
|
180
214
|
*
|
|
181
215
|
* @param guard Guard or refinement to negate.
|
|
182
216
|
* @returns Refinement excluding the guarded subtype from the input type.
|
|
217
|
+
* @example
|
|
218
|
+
* const isPresent = not(isNil);
|
|
183
219
|
*/
|
|
184
220
|
declare function not<A, T>(guard: Guard<T>): Refine<A, Exclude<A, Extract<A, T>>>;
|
|
185
221
|
declare function not<A, B extends A>(refine: Refine<A, B>): Refine<A, Exclude<A, B>>;
|
|
186
222
|
|
|
187
223
|
/**
|
|
188
224
|
* Allows `null` in addition to values accepted by the given guard/refinement.
|
|
225
|
+
* Use this instead of hand-writing `value === null || guard(value)` when the
|
|
226
|
+
* widened predicate should stay reusable.
|
|
189
227
|
*
|
|
190
228
|
* @param guard Guard/refinement for the base type.
|
|
191
229
|
* @returns Guard/refinement widened to include `null`.
|
|
230
|
+
* @example
|
|
231
|
+
* const isNullableString = nullable(isString);
|
|
232
|
+
* @see nullish
|
|
192
233
|
*/
|
|
193
234
|
declare function nullable<T>(guard: Guard<T>): Guard<T | null>;
|
|
194
235
|
declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null, B | null>;
|
|
@@ -197,22 +238,33 @@ declare function nullable<A, B extends A>(refine: Refine<A, B>): Refine<A | null
|
|
|
197
238
|
*
|
|
198
239
|
* @param guard Guard/refinement possibly including `null`.
|
|
199
240
|
* @returns Guard/refinement with `null` excluded.
|
|
241
|
+
* @example
|
|
242
|
+
* const isNonNullString = nonNull(nullable(isString));
|
|
200
243
|
*/
|
|
201
244
|
declare function nonNull<T>(guard: Guard<T>): Guard<Exclude<T, null>>;
|
|
202
245
|
declare function nonNull<A, B extends A>(refine: Refine<A, B>): Refine<Exclude<A, null>, Exclude<B, null>>;
|
|
203
246
|
/**
|
|
204
247
|
* Allows `null` or `undefined` in addition to values accepted by the guard.
|
|
248
|
+
* Use this instead of hand-writing `isNil(value) || guard(value)` when widening
|
|
249
|
+
* another guard.
|
|
205
250
|
*
|
|
206
251
|
* @param guard Guard/refinement for the base type.
|
|
207
252
|
* @returns Guard/refinement widened to include `null | undefined`.
|
|
253
|
+
* @example
|
|
254
|
+
* const isNullishString = nullish(isString);
|
|
255
|
+
* @see isNil
|
|
208
256
|
*/
|
|
209
257
|
declare function nullish<T>(guard: Guard<T>): Guard<T | null | undefined>;
|
|
210
258
|
declare function nullish<A, B extends A>(refine: Refine<A, B>): Refine<A | null | undefined, B | null | undefined>;
|
|
211
259
|
/**
|
|
212
260
|
* Allows `undefined` in addition to values accepted by the guard.
|
|
261
|
+
* Use this for value-level optionality. For optional object keys in `struct`,
|
|
262
|
+
* use `optionalKey(...)`.
|
|
213
263
|
*
|
|
214
264
|
* @param guard Guard/refinement for the base type.
|
|
215
265
|
* @returns Guard/refinement widened to include `undefined`.
|
|
266
|
+
* @example
|
|
267
|
+
* const isOptionalString = optional(isString);
|
|
216
268
|
*/
|
|
217
269
|
declare function optional<T>(guard: Guard<T>): Guard<T | undefined>;
|
|
218
270
|
declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | undefined, B | undefined>;
|
|
@@ -221,6 +273,8 @@ declare function optional<A, B extends A>(refine: Refine<A, B>): Refine<A | unde
|
|
|
221
273
|
*
|
|
222
274
|
* @param guard Guard/refinement that may accept `undefined`.
|
|
223
275
|
* @returns Guard/refinement with `undefined` removed.
|
|
276
|
+
* @example
|
|
277
|
+
* const isRequiredString = required(optional(isString));
|
|
224
278
|
*/
|
|
225
279
|
declare function required<T>(guard: Guard<T | undefined>): Guard<T>;
|
|
226
280
|
declare function required<A, B extends A>(refine: Refine<A | undefined, B | undefined>): Refine<A, B>;
|
|
@@ -351,17 +405,21 @@ declare const isSymbol: Predicate<symbol>;
|
|
|
351
405
|
/**
|
|
352
406
|
* Checks whether a value is exactly `undefined`.
|
|
353
407
|
*
|
|
354
|
-
*
|
|
408
|
+
* For reusable nullish checks, use {@link isNil} or compose a named guard with
|
|
409
|
+
* {@link or} once and reuse it.
|
|
355
410
|
*
|
|
356
411
|
* @returns Predicate narrowing to `undefined`.
|
|
412
|
+
* @see isNil
|
|
357
413
|
*/
|
|
358
414
|
declare const isUndefined: Predicate<undefined>;
|
|
359
415
|
/**
|
|
360
416
|
* Checks whether a value is exactly `null`.
|
|
361
417
|
*
|
|
362
|
-
*
|
|
418
|
+
* For reusable nullish checks, use {@link isNil} or compose a named guard with
|
|
419
|
+
* {@link or} once and reuse it.
|
|
363
420
|
*
|
|
364
421
|
* @returns Predicate narrowing to `null`.
|
|
422
|
+
* @see isNil
|
|
365
423
|
*/
|
|
366
424
|
declare const isNull: Predicate<null>;
|
|
367
425
|
/**
|
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) &&
|
|
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) &&
|
|
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) &&
|
|
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]) =>
|
|
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(
|
|
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 (!
|
|
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) &&
|
|
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) &&
|
|
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) &&
|
|
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]) =>
|
|
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(
|
|
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 (!
|
|
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.
|
|
3
|
+
"version": "1.11.1",
|
|
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
|
],
|