is-kit 1.7.4 → 1.8.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 CHANGED
@@ -309,22 +309,33 @@ if (isAdmin(value)) {
309
309
 
310
310
  Here are the kinds of problems `is-kit` is especially good at solving:
311
311
 
312
- ### API response checks
312
+ ### Typed object guard checks
313
313
 
314
314
  ```ts
315
- import { isNumber, isString, safeParse, struct } from 'is-kit';
315
+ import { isNumber, isString, optionalKey, safeParse, typedStruct } from 'is-kit';
316
316
 
317
- const isPost = struct({
317
+ type PostResponse = ApiResponse<'/posts/{id}', 'get'>;
318
+
319
+ const isPost = typedStruct<PostResponse>()({
318
320
  id: isNumber,
319
- title: isString
321
+ title: isString,
322
+ summary: optionalKey(isString)
320
323
  });
321
324
 
325
+ const payload: unknown = await fetchPost();
322
326
  const parsed = safeParse(isPost, payload);
327
+
323
328
  if (parsed.valid) {
324
329
  renderPost(parsed.value);
325
330
  }
326
331
  ```
327
332
 
333
+ `typedStruct<T>()` is a small helper for keeping hand-written `struct` guards in
334
+ sync with an existing object type. Optional keys in `T` must still be declared
335
+ with `optionalKey(...)`; this makes drift visible when the target type changes.
336
+ OpenAPI-generated response types are one useful case, but the helper is not an
337
+ OpenAPI validator or schema generator.
338
+
328
339
  ### Safe array filtering
329
340
 
330
341
  ```ts
package/dist/index.d.mts CHANGED
@@ -31,20 +31,26 @@ type SchemaField = Predicate<unknown> | OptionalSchemaField<Predicate<unknown>>;
31
31
  * Object schema consumed by `struct`.
32
32
  */
33
33
  type Schema = Readonly<Record<string, SchemaField>>;
34
+ /**
35
+ * Object schema shape preserving concrete keys.
36
+ */
37
+ type SchemaShape<S extends object> = Readonly<{
38
+ [K in keyof S]: SchemaField;
39
+ }>;
34
40
  type Simplify<T> = {
35
41
  [K in keyof T]: T[K];
36
42
  };
37
43
  type InferSchemaField<F> = F extends Predicate<unknown> ? GuardedOf<F> : F extends OptionalSchemaField<infer G> ? GuardedOf<G> : never;
38
- type RequiredSchemaKeys<S extends Schema> = {
44
+ type RequiredSchemaKeys<S extends SchemaShape<S>> = {
39
45
  [K in keyof S]-?: S[K] extends OptionalSchemaField<Predicate<unknown>> ? never : K;
40
46
  }[keyof S];
41
- type OptionalSchemaKeys<S extends Schema> = {
47
+ type OptionalSchemaKeys<S extends SchemaShape<S>> = {
42
48
  [K in keyof S]-?: S[K] extends OptionalSchemaField<Predicate<unknown>> ? K : never;
43
49
  }[keyof S];
44
50
  /**
45
51
  * Infers the readonly object type produced by a `struct` schema.
46
52
  */
47
- type InferSchema<S extends Schema> = Simplify<{
53
+ type InferSchema<S extends SchemaShape<S>> = Simplify<{
48
54
  readonly [K in RequiredSchemaKeys<S>]: InferSchemaField<S[K]>;
49
55
  } & {
50
56
  readonly [K in OptionalSchemaKeys<S>]?: InferSchemaField<S[K]>;
@@ -549,10 +555,34 @@ declare function optionalKey<G extends Predicate<unknown>>(guard: G): OptionalSc
549
555
  * @param options When `{ exact: true }`, disallows properties not in `schema`.
550
556
  * @returns Predicate that narrows to the inferred struct type.
551
557
  */
552
- declare function struct<S extends Schema>(schema: S, options?: {
558
+ declare function struct<const S extends SchemaShape<S>>(schema: S, options?: {
553
559
  exact?: boolean;
554
560
  }): Predicate<InferSchema<S>>;
555
561
 
562
+ type OptionalKeys<T> = {
563
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
564
+ }[keyof T];
565
+ type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
566
+ type TypedStructShape<T extends object> = {
567
+ readonly [K in Extract<RequiredKeys<T>, string>]-?: Predicate<T[K]>;
568
+ } & {
569
+ readonly [K in Extract<OptionalKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
570
+ };
571
+ type NoExtraKeys<S, Shape> = S & {
572
+ readonly [K in Exclude<keyof S, keyof Shape>]: never;
573
+ };
574
+ type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
575
+ /**
576
+ * Creates a `struct` builder checked against an existing object type.
577
+ * Optional target keys must also be declared with `optionalKey` so type drift
578
+ * stays visible when the target type changes.
579
+ *
580
+ * @returns Builder that rejects missing, extra, or incompatible struct fields.
581
+ */
582
+ declare function typedStruct<T extends object>(): <const S extends TypedStructShape<T>>(fields: TypedStructFields<T, S>, options?: {
583
+ exact?: boolean;
584
+ }) => Predicate<InferSchema<TypedStructFields<T, S>>>;
585
+
556
586
  /**
557
587
  * Validates a fixed-length tuple by applying element-wise guards.
558
588
  *
@@ -614,4 +644,4 @@ declare const everyOwnEnumerableEntry: (values: Record<string, unknown>, keyPred
614
644
  */
615
645
  declare const toBooleanPredicates: <A>(predicates: readonly ((value: A) => boolean)[]) => ReadonlyArray<(value: A) => boolean>;
616
646
 
617
- 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, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf };
647
+ 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, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
package/dist/index.d.ts CHANGED
@@ -31,20 +31,26 @@ type SchemaField = Predicate<unknown> | OptionalSchemaField<Predicate<unknown>>;
31
31
  * Object schema consumed by `struct`.
32
32
  */
33
33
  type Schema = Readonly<Record<string, SchemaField>>;
34
+ /**
35
+ * Object schema shape preserving concrete keys.
36
+ */
37
+ type SchemaShape<S extends object> = Readonly<{
38
+ [K in keyof S]: SchemaField;
39
+ }>;
34
40
  type Simplify<T> = {
35
41
  [K in keyof T]: T[K];
36
42
  };
37
43
  type InferSchemaField<F> = F extends Predicate<unknown> ? GuardedOf<F> : F extends OptionalSchemaField<infer G> ? GuardedOf<G> : never;
38
- type RequiredSchemaKeys<S extends Schema> = {
44
+ type RequiredSchemaKeys<S extends SchemaShape<S>> = {
39
45
  [K in keyof S]-?: S[K] extends OptionalSchemaField<Predicate<unknown>> ? never : K;
40
46
  }[keyof S];
41
- type OptionalSchemaKeys<S extends Schema> = {
47
+ type OptionalSchemaKeys<S extends SchemaShape<S>> = {
42
48
  [K in keyof S]-?: S[K] extends OptionalSchemaField<Predicate<unknown>> ? K : never;
43
49
  }[keyof S];
44
50
  /**
45
51
  * Infers the readonly object type produced by a `struct` schema.
46
52
  */
47
- type InferSchema<S extends Schema> = Simplify<{
53
+ type InferSchema<S extends SchemaShape<S>> = Simplify<{
48
54
  readonly [K in RequiredSchemaKeys<S>]: InferSchemaField<S[K]>;
49
55
  } & {
50
56
  readonly [K in OptionalSchemaKeys<S>]?: InferSchemaField<S[K]>;
@@ -549,10 +555,34 @@ declare function optionalKey<G extends Predicate<unknown>>(guard: G): OptionalSc
549
555
  * @param options When `{ exact: true }`, disallows properties not in `schema`.
550
556
  * @returns Predicate that narrows to the inferred struct type.
551
557
  */
552
- declare function struct<S extends Schema>(schema: S, options?: {
558
+ declare function struct<const S extends SchemaShape<S>>(schema: S, options?: {
553
559
  exact?: boolean;
554
560
  }): Predicate<InferSchema<S>>;
555
561
 
562
+ type OptionalKeys<T> = {
563
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
564
+ }[keyof T];
565
+ type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
566
+ type TypedStructShape<T extends object> = {
567
+ readonly [K in Extract<RequiredKeys<T>, string>]-?: Predicate<T[K]>;
568
+ } & {
569
+ readonly [K in Extract<OptionalKeys<T>, string>]-?: OptionalSchemaField<Predicate<T[K]>>;
570
+ };
571
+ type NoExtraKeys<S, Shape> = S & {
572
+ readonly [K in Exclude<keyof S, keyof Shape>]: never;
573
+ };
574
+ type TypedStructFields<T extends object, S extends TypedStructShape<T>> = NoExtraKeys<S, TypedStructShape<T>>;
575
+ /**
576
+ * Creates a `struct` builder checked against an existing object type.
577
+ * Optional target keys must also be declared with `optionalKey` so type drift
578
+ * stays visible when the target type changes.
579
+ *
580
+ * @returns Builder that rejects missing, extra, or incompatible struct fields.
581
+ */
582
+ declare function typedStruct<T extends object>(): <const S extends TypedStructShape<T>>(fields: TypedStructFields<T, S>, options?: {
583
+ exact?: boolean;
584
+ }) => Predicate<InferSchema<TypedStructFields<T, S>>>;
585
+
556
586
  /**
557
587
  * Validates a fixed-length tuple by applying element-wise guards.
558
588
  *
@@ -614,4 +644,4 @@ declare const everyOwnEnumerableEntry: (values: Record<string, unknown>, keyPred
614
644
  */
615
645
  declare const toBooleanPredicates: <A>(predicates: readonly ((value: A) => boolean)[]) => ReadonlyArray<(value: A) => boolean>;
616
646
 
617
- 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, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf };
647
+ 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, isFiniteNumber, isFunction, isInfiniteNumber, isInstanceOf, isInteger, isIterable, isMap, isNaN, isNegative, isNull, isNumber, isNumberPrimitive, isObject, isPlainObject, isPositive, isPrimitive, isPromiseLike, isRegExp, isSafeInteger, isSet, isString, isSymbol, isTypedArray, isURL, isUndefined, isWeakMap, isWeakSet, isZero, mapOf, narrowKeyTo, nonEmptyArrayOf, nonNull, not, nullable, nullish, oneOf, oneOfValues, optional, optionalKey, or, predicateToRefine, recordOf, required, safeParse, safeParseWith, setOf, struct, toBooleanPredicates, tupleOf, typedStruct };
package/dist/index.js CHANGED
@@ -93,7 +93,8 @@ __export(index_exports, {
93
93
  setOf: () => setOf,
94
94
  struct: () => struct,
95
95
  toBooleanPredicates: () => toBooleanPredicates,
96
- tupleOf: () => tupleOf
96
+ tupleOf: () => tupleOf,
97
+ typedStruct: () => typedStruct
97
98
  });
98
99
  module.exports = __toCommonJS(index_exports);
99
100
 
@@ -458,7 +459,9 @@ function struct(schema, options) {
458
459
  const requiredEntries = [];
459
460
  const optionalEntries = [];
460
461
  const schemaKeys = [];
461
- for (const [key, field] of Object.entries(schema)) {
462
+ for (const key in schema) {
463
+ if (!Object.hasOwn(schema, key)) continue;
464
+ const field = schema[key];
462
465
  schemaKeys.push(key);
463
466
  if (isOptionalSchemaField(field)) {
464
467
  optionalEntries.push([key, field.guard]);
@@ -477,6 +480,15 @@ function struct(schema, options) {
477
480
  });
478
481
  }
479
482
 
483
+ // src/core/combinators/typed-struct.ts
484
+ function typedStruct() {
485
+ return (fields, options) => (
486
+ // WHY: `typedStruct` keeps `struct` runtime behavior while using the target
487
+ // type only to check that hand-written guard fields stay in sync.
488
+ struct(fields, options)
489
+ );
490
+ }
491
+
480
492
  // src/core/combinators/tuple.ts
481
493
  function tupleOf(...guards) {
482
494
  return define(
@@ -558,5 +570,6 @@ function tupleOf(...guards) {
558
570
  setOf,
559
571
  struct,
560
572
  toBooleanPredicates,
561
- tupleOf
573
+ tupleOf,
574
+ typedStruct
562
575
  });
package/dist/index.mjs CHANGED
@@ -359,7 +359,9 @@ function struct(schema, options) {
359
359
  const requiredEntries = [];
360
360
  const optionalEntries = [];
361
361
  const schemaKeys = [];
362
- for (const [key, field] of Object.entries(schema)) {
362
+ for (const key in schema) {
363
+ if (!Object.hasOwn(schema, key)) continue;
364
+ const field = schema[key];
363
365
  schemaKeys.push(key);
364
366
  if (isOptionalSchemaField(field)) {
365
367
  optionalEntries.push([key, field.guard]);
@@ -378,6 +380,15 @@ function struct(schema, options) {
378
380
  });
379
381
  }
380
382
 
383
+ // src/core/combinators/typed-struct.ts
384
+ function typedStruct() {
385
+ return (fields, options) => (
386
+ // WHY: `typedStruct` keeps `struct` runtime behavior while using the target
387
+ // type only to check that hand-written guard fields stay in sync.
388
+ struct(fields, options)
389
+ );
390
+ }
391
+
381
392
  // src/core/combinators/tuple.ts
382
393
  function tupleOf(...guards) {
383
394
  return define(
@@ -458,5 +469,6 @@ export {
458
469
  setOf,
459
470
  struct,
460
471
  toBooleanPredicates,
461
- tupleOf
472
+ tupleOf,
473
+ typedStruct
462
474
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "is-kit",
3
- "version": "1.7.4",
3
+ "version": "1.8.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",