objection 4.0.0-rc.2 → 4.0.0-rc.4

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.
@@ -18,7 +18,8 @@ import { Knex } from 'knex';
18
18
  // Only the declarations marked with `export` are exported.
19
19
  export {};
20
20
 
21
- // Phantom brand of the relation types, used by `TypedRelationMappings`.
21
+ // Phantom brand of the relation types, used by `TypedRelationMappings` and
22
+ // `FromModel`.
22
23
  declare const relationKind: unique symbol;
23
24
  // Phantom brand of `Generated` properties.
24
25
  declare const generated: unique symbol;
@@ -197,7 +198,7 @@ export interface CallbackVoid<T> {
197
198
  }
198
199
 
199
200
  export type Identity<T> = (value: T) => T;
200
- export type AnyQueryBuilder = QueryBuilder<any, any>;
201
+ export type AnyQueryBuilder = QueryBuilder<any, any, any>;
201
202
  export type AnyModelConstructor = ModelConstructor<Model>;
202
203
  export type ModifierFunction<QB extends AnyQueryBuilder> = (qb: QB, ...args: any[]) => void;
203
204
  export type Modifier<QB extends AnyQueryBuilder = AnyQueryBuilder> =
@@ -398,7 +399,7 @@ export type WithGraphTreeQueryBuilder<QB extends AnyQueryBuilder, T> = [keyof T]
398
399
  ? QB
399
400
  : WithResultKind<
400
401
  QB,
401
- WithGraph<ModelType<QB>, T>['QueryBuilderType'],
402
+ KeepExtra<WithGraph<ModelType<QB>, T>['QueryBuilderType'], ExtraType<QB>>,
402
403
  ModelType<QB>,
403
404
  ResultType<QB>,
404
405
  T
@@ -474,11 +475,12 @@ export type ItemType<T> = T extends Array<unknown> ? T[number] : T;
474
475
  * Type for keys of non-function properties of T.
475
476
  */
476
477
  export type NonFunctionPropertyNames<T> = {
477
- [K in keyof T]: T[K] extends Function ? never : K;
478
+ [K in keyof T]-?: T[K] extends Function ? never : K;
478
479
  }[keyof T];
479
480
 
480
- // The type-only members of models, which aren't data.
481
- type ModelTypeMembers = 'QueryBuilderType' | 'ColumnNameMapping';
481
+ // The type-only members of models, which aren't data, also the keys of the
482
+ // virtual attributes that `FromModel` adds.
483
+ type ModelTypeMembers = 'QueryBuilderType' | 'ColumnNameMapping' | `~virtual:${string}`;
482
484
 
483
485
  /**
484
486
  * Type that attempts to only select the user-defined model properties.
@@ -501,15 +503,24 @@ export type ModelObject<T extends Model> = Pick<T, DataPropertyNames<T>>;
501
503
 
502
504
  /**
503
505
  * The JSON of model M, as `toJSON()` returns it: its properties without
504
- * methods, and the related models as JSON too. Values aren't converted, e.g.
505
- * dates stay dates.
506
+ * methods, the results of the methods in `virtualAttributes` on models with
507
+ * `FromModel`, and the related models as JSON too. Values aren't converted,
508
+ * e.g. dates stay dates.
506
509
  */
507
510
  export type ModelJson<M> = {
508
- [K in keyof Pick<M, DataPropertyNames<M> & keyof M>]: K extends RelationPropertyNames<M>
511
+ [
512
+ K in keyof Pick<M, (DataPropertyNames<M> | VirtualAttributeNames<M>) & keyof M>
513
+ ]: K extends RelationPropertyNames<M>
509
514
  ? RelationJson<M[K]>
510
- : M[K];
515
+ : M[K] extends (...args: any) => infer R
516
+ ? RelationJson<R>
517
+ : M[K];
511
518
  };
512
519
 
520
+ type VirtualAttributeNames<M> = {
521
+ [K in keyof M]: K extends `~virtual:${infer N}` ? N : never;
522
+ }[keyof M];
523
+
513
524
  type RelationJson<P> = P extends (infer I)[]
514
525
  ? ModelJson<I>[]
515
526
  : P extends readonly (infer I)[]
@@ -529,7 +540,7 @@ export type PartialModelObject<T extends Model> = {
529
540
  ? I[] | PartialModelObject<I>[]
530
541
  : Expression<T[K]>
531
542
  : NonNullable<T[K]> extends Model
532
- ? Expression<T[K]> | PartialModelObject<NonNullable<T[K]>> // nullable to-one relations
543
+ ? Expression<T[K]> | PartialModelObject<NonNullable<T[K]>> // Nullable to-one relations
533
544
  : Expression<T[K]>;
534
545
  };
535
546
 
@@ -580,7 +591,7 @@ export type GraphParameters = {
580
591
  export type PartialModelGraph<M, T = M & GraphParameters> = T extends any
581
592
  ? {
582
593
  [K in DataPropertyNames<T>]?: null extends T[K]
583
- ? PartialModelGraphField<NonNullable<T[K]>> | null // handle nullable BelongsToOneRelations
594
+ ? PartialModelGraphField<NonNullable<T[K]>> | null // Strip null to recognize to-one relations
584
595
  : PartialModelGraphField<T[K]>;
585
596
  }
586
597
  : never;
@@ -624,9 +635,10 @@ export type OptionalInsertPropertyNames<M> = {
624
635
  export type InsertablePropertyNames<M> = Exclude<DataPropertyNames<M>, RelationPropertyNames<M>>;
625
636
 
626
637
  /**
627
- * The data to insert a model M with `insert()`. Unlike `PartialModelObject`,
628
- * all properties are required except optional, nullable and `Generated`
629
- * ones. Relation properties are left out.
638
+ * The complete data of a new row of model M, to check what is passed to
639
+ * `insert()`, which itself takes any subset of the properties. Unlike
640
+ * `PartialModelObject`, all properties are required except optional, nullable
641
+ * and `Generated` ones. Relation properties are left out.
630
642
  */
631
643
  export type Insertable<M extends Model> = {
632
644
  [K in Exclude<InsertablePropertyNames<M>, OptionalInsertPropertyNames<M>>]: Expression<M[K]>;
@@ -635,9 +647,9 @@ export type Insertable<M extends Model> = {
635
647
  };
636
648
 
637
649
  /**
638
- * Like `Insertable`, but for `insertGraph()`: relation properties are
639
- * optional and hold the insertable graphs of the related models, or
640
- * references to existing ones through `#dbRef` or `#ref`.
650
+ * Like `Insertable`, but for graphs passed to `insertGraph()`: relation
651
+ * properties are optional and hold the insertable graphs of the related
652
+ * models, or references to existing ones through `#dbRef` or `#ref`.
641
653
  */
642
654
  export type InsertableGraph<M extends Model> = Insertable<M> &
643
655
  Omit<GraphParameters, '#dbRef' | '#ref'> & {
@@ -656,38 +668,15 @@ export type InsertableGraphField<F> =
656
668
  /**
657
669
  * Extracts the property names (excluding relations) of a model class.
658
670
  */
659
- export type ModelProps<T extends Model> = Exclude<
660
- {
661
- [K in keyof T]?: Defined<T[K]> extends Model
662
- ? never
663
- : Defined<T[K]> extends Array<infer I>
664
- ? I extends Model
665
- ? never
666
- : K
667
- : T[K] extends Function
668
- ? never
669
- : K;
670
- }[keyof T],
671
- undefined | ModelTypeMembers
672
- >;
671
+ export type ModelProps<T extends Model> = InsertablePropertyNames<T>;
673
672
 
674
673
  /**
675
- * Extracts the relation names of the a model class.
674
+ * Extracts the relation names of a model class.
676
675
  */
677
- export type ModelRelations<T extends Model> = Defined<
678
- {
679
- [K in keyof T]?: Defined<T[K]> extends Model
680
- ? K
681
- : Defined<T[K]> extends Array<infer I>
682
- ? I extends Model
683
- ? K
684
- : never
685
- : never;
686
- }[keyof T]
687
- >;
676
+ export type ModelRelations<T extends Model> = RelationPropertyNames<T>;
688
677
 
689
678
  /**
690
- * Given a model property type, returns a query builer type of
679
+ * Given a model property type, returns a query builder type of the
691
680
  * correct kind if the property is a model or a model array. Queries for
692
681
  * to-one relations resolve to `undefined` when there's no related row.
693
682
  */
@@ -728,16 +717,46 @@ export type ModelType<T extends { ModelType: any }> = T['ModelType'];
728
717
  export type ResultType<T extends { ResultType: any }> = T['ResultType'];
729
718
 
730
719
  /**
731
- * Re-instantiates the query builder QB with the model type M and the result
732
- * type R, through its `RebindType`. The `~rebind` slot is `unknown`, so the
733
- * intersection resolves it to exactly `[M, R]`. Rebinding QB's own
734
- * `RebindType` instead of QB keeps the intersection small, which TypeScript
735
- * checks much faster.
720
+ * Re-instantiates the query builder QB with the model type M, the result
721
+ * type R and the extra type X, through its `RebindType`. The `~rebind` slot is
722
+ * `unknown`, so the intersection resolves it to exactly `[M, R, X]`.
723
+ * Rebinding QB's own `RebindType` instead of QB keeps the intersection small,
724
+ * which TypeScript checks much faster.
736
725
  */
737
- type Rebind<QB extends AnyQueryBuilder, M extends Model, R> = (QB['RebindType'] & {
738
- '~rebind': [M, R];
726
+ type Rebind<
727
+ QB extends AnyQueryBuilder,
728
+ M extends Model,
729
+ R,
730
+ X = IfStrict<unknown, ExtraType<QB>>,
731
+ > = (QB['RebindType'] & {
732
+ '~rebind': [M, R, X];
739
733
  })['RebindType'];
740
734
 
735
+ /**
736
+ * Gets the extra type from a query builder type: what the types know about
737
+ * the query beyond its model and result, see `QueryExtra`.
738
+ */
739
+ export type ExtraType<T extends { ExtraType: any }> = T['ExtraType'];
740
+
741
+ /**
742
+ * The extra type of queries in strict mode: the model class C that started the
743
+ * query, e.g. with `Person.query()`, and the modifiers P registered with
744
+ * `modifiers()`, to check the names of modifiers, and the id I of the class,
745
+ * for `findById()` and the other `*ById()` methods.
746
+ */
747
+ export interface QueryExtra<C = unknown, P = unknown, I = MaybeCompositeId> {
748
+ modelClass: C;
749
+ registered: P;
750
+ id: I;
751
+ }
752
+
753
+ type WithExtra<QB extends AnyQueryBuilder, X> = Rebind<QB, ModelType<QB>, ResultType<QB>, X>;
754
+
755
+ // The narrowed query builders of `withGraph*()` start without the extra type
756
+ // of QB, so it is passed on, if there is one.
757
+ type KeepExtra<NQB extends AnyQueryBuilder, X> =
758
+ X extends QueryExtra<any, any, any> ? WithExtra<NQB, X> : NQB;
759
+
741
760
  /**
742
761
  * Gets the single item query builder type for a query builder.
743
762
  */
@@ -763,18 +782,19 @@ export type NumberQueryBuilder<QB extends AnyQueryBuilder> = QB['~number'];
763
782
  */
764
783
  export type PageQueryBuilder<QB extends AnyQueryBuilder> = QB['~page'];
765
784
 
766
- // Strict mode, on by default, checks the column names and values of queries
767
- // against the models. Augment `TypeConfig` to opt out:
768
- //
769
- // declare module 'objection' {
770
- // interface TypeConfig {
771
- // strict: false;
772
- // }
773
- // }
774
-
785
+ /**
786
+ * Strict mode checks the column names and values of queries against the
787
+ * models. Augment `TypeConfig` to opt in:
788
+ *
789
+ * declare module 'objection' {
790
+ * interface TypeConfig {
791
+ * strict: true;
792
+ * }
793
+ * }
794
+ */
775
795
  export interface TypeConfig {}
776
796
 
777
- type IsStrict = TypeConfig extends { strict: false } ? false : true;
797
+ type IsStrict = TypeConfig extends { strict: true } ? true : false;
778
798
 
779
799
  // As IsStrict isn't generic, IfStrict resolves eagerly.
780
800
  type IfStrict<Loose, Strict> = IsStrict extends true ? Strict : Loose;
@@ -890,10 +910,11 @@ type CheckedColumn<M, C extends string> =
890
910
  IsConcreteModel<M> extends true ? CheckColumn<M, C> : string;
891
911
 
892
912
  /**
893
- * A column argument: checked against model M in strict mode, any column in
894
- * loose mode, where the model's properties come first, for autocompletion.
913
+ * A column argument: checked against model M in strict mode, the model's
914
+ * properties in loose mode, for autocompletion. In loose mode, other columns
915
+ * go through the RefArg overloads.
895
916
  */
896
- type ColumnArg<M, C extends string> = IfStrict<ModelProps<M & Model>, CheckedColumn<M, C>>;
917
+ type ColumnArg<M, C extends string> = IfStrict<InsertablePropertyNames<M>, CheckedColumn<M, C>>;
897
918
 
898
919
  /**
899
920
  * The value of a column of type T in conditions: `null` is always accepted,
@@ -1042,8 +1063,8 @@ type StrictSelectionObject<M, O> = {
1042
1063
  /**
1043
1064
  * The query builder QB with its result rows narrowed to the properties P in
1044
1065
  * strict mode, next to the model's methods and relations, as after `select()`
1045
- * and the aggregates, marked with SelectedBrand. Repeated selections add up. Queries
1046
- * that aren't checked, e.g. in generic code, aren't narrowed.
1066
+ * and the aggregates, marked with SelectedBrand. Repeated selections add up.
1067
+ * Queries that aren't checked, e.g. in generic code, aren't narrowed.
1047
1068
  */
1048
1069
  type NarrowRows<QB extends AnyQueryBuilder, P, All extends boolean = false> = RowsQueryBuilder<
1049
1070
  QB,
@@ -1224,9 +1245,9 @@ type SelectedType<M, C extends string> = C extends `${infer Q}.${infer Col}`
1224
1245
  : ColumnType<M, C>;
1225
1246
 
1226
1247
  /**
1227
- * The property of the column or alias C in the result rows: `snakeCaseMappers()`
1228
- * converts them to camel case, with `upperCase: true` after lowering the case
1229
- * of names in upper case.
1248
+ * The property of the column or alias C in the result rows:
1249
+ * `snakeCaseMappers()` converts them to camel case, with `upperCase: true`
1250
+ * after lowering the case of names in upper case.
1230
1251
  */
1231
1252
  type ResultKey<M, C extends string> = M extends { ColumnNameMapping: 'snakeCase' }
1232
1253
  ? CamelCase<C>
@@ -1276,17 +1297,222 @@ type UnknownRelations<M, T> = {
1276
1297
 
1277
1298
  /**
1278
1299
  * In strict mode, relation expressions are intersected with this, to reject
1279
- * the names of unknown relations.
1300
+ * the names of unknown relations, and of unknown modifiers, checked against
1301
+ * the related class of each node and the modifiers registered on query
1302
+ * builder QB.
1280
1303
  */
1281
- type CheckRelationExpression<M, E> = IfStrict<
1304
+ type CheckRelationExpression<M, E, QB extends AnyQueryBuilder = never> = IfStrict<
1282
1305
  unknown,
1283
1306
  IsConcreteModel<M> extends true
1284
1307
  ? [UnknownRelations<M, ParseRelationExpression<E>>] extends [never]
1285
- ? unknown
1308
+ ? CheckModifierExpression<E, QB>
1286
1309
  : { '~unknownRelation': UnknownRelations<M, ParseRelationExpression<E>> }
1287
1310
  : unknown
1288
1311
  >;
1289
1312
 
1313
+ type CheckModifierExpression<E, QB extends AnyQueryBuilder> =
1314
+ HasQueryClass<QB> extends true
1315
+ ? [CheckedModifiers<E, QB>] extends [never]
1316
+ ? unknown
1317
+ : { '~unknownModifier': CheckedModifiers<E, QB> }
1318
+ : unknown;
1319
+
1320
+ // Queries in generic code, where the model class can't be resolved, aren't
1321
+ // checked.
1322
+ type HasQueryClass<QB extends AnyQueryBuilder> =
1323
+ IsConcreteModel<ModelType<QB>> extends false
1324
+ ? false
1325
+ : [QueryClass<QB>] extends [never]
1326
+ ? false
1327
+ : true;
1328
+
1329
+ // Only expressions with modifiers are checked for them.
1330
+ type CheckedModifiers<E, QB> = E extends `${string}(${string}` | object
1331
+ ? UnknownModifiers<
1332
+ ClassMappings<QueryClass<QB>>,
1333
+ ParseModifierExpression<E>,
1334
+ keyof RegisteredModifiers<QB>
1335
+ >
1336
+ : never;
1337
+
1338
+ /**
1339
+ * The modifiers that a modifier name can refer to on query builder QB: the
1340
+ * `modifiers` of the model class that started the query, and those registered
1341
+ * with `modifiers()` before. Unknown names are only rejected in strict mode,
1342
+ * and only on queries of model classes with literal `modifiers`.
1343
+ */
1344
+ type KnownModifiers<QB> = ClassModifiers<QueryClass<QB>> & RegisteredModifiers<QB>;
1345
+
1346
+ // The model class that started the query, if any.
1347
+ type QueryClass<QB> = [QB] extends [never]
1348
+ ? never
1349
+ : QB extends { ExtraType: QueryExtra<infer C, any, any> }
1350
+ ? unknown extends C
1351
+ ? never
1352
+ : C
1353
+ : never;
1354
+
1355
+ type ClassModifiers<C> = C extends { modifiers: infer O } ? O : {};
1356
+
1357
+ type ClassMappings<C> = C extends { relationMappings: infer T }
1358
+ ? T extends () => infer R
1359
+ ? R
1360
+ : T
1361
+ : {};
1362
+
1363
+ type RegisteredModifiers<QB> = [QB] extends [never]
1364
+ ? {}
1365
+ : QB extends { ExtraType: { registered: infer P } }
1366
+ ? P
1367
+ : {};
1368
+
1369
+ /**
1370
+ * The id that `findById()` and the other `*ById()` methods take on query
1371
+ * builder QB: in strict mode, the type of the property of the class's literal
1372
+ * `idColumn`, or a tuple of them for composite ids, e.g. with
1373
+ * `static idColumn = ['groupId', 'memberId'] as const`.
1374
+ */
1375
+ type IdArg<QB extends AnyQueryBuilder> = IfStrict<
1376
+ MaybeCompositeId,
1377
+ [QueryClass<QB>] extends [never]
1378
+ ? MaybeCompositeId
1379
+ : QB extends { ExtraType: QueryExtra<any, any, infer I> }
1380
+ ? I
1381
+ : MaybeCompositeId
1382
+ >;
1383
+
1384
+ // The id of a related class, read from its instances.
1385
+ type ClassId<C> = C extends { idColumn: infer K; prototype: infer M }
1386
+ ? ColumnsId<K, M>
1387
+ : MaybeCompositeId;
1388
+
1389
+ // The inherited `idColumn` and other general types give any id.
1390
+ type ColumnsId<K, M> = [K] extends [string]
1391
+ ? string extends K
1392
+ ? MaybeCompositeId
1393
+ : PropertyId<M, K, MaybeCompositeId>
1394
+ : [K] extends [readonly string[]]
1395
+ ? string extends K[number]
1396
+ ? MaybeCompositeId
1397
+ : { readonly [I in keyof K]: PropertyId<M, K[I] & string, Id> }
1398
+ : MaybeCompositeId;
1399
+
1400
+ // The id column is a database column, named after the property by the
1401
+ // model's `ColumnNameMapping`. Columns without a property give the fallback F.
1402
+ type PropertyId<M, C extends string, F> =
1403
+ ResultKey<M, C> extends infer P extends keyof M ? NonNullable<M[P]> : F;
1404
+
1405
+ // Modifiers with their general type, e.g. inherited from `Model`, aren't
1406
+ // checked.
1407
+ type ChecksModifiers<QB extends AnyQueryBuilder> = IfStrict<
1408
+ false,
1409
+ HasQueryClass<QB> extends true ? ChecksClassModifiers<ClassModifiers<QueryClass<QB>>> : false
1410
+ >;
1411
+
1412
+ type ChecksClassModifiers<O> = string extends keyof O ? false : true;
1413
+
1414
+ // The names that `modify()` takes on query builder QB, also in lists. Names
1415
+ // in variables typed `string` are rejected where they are checked.
1416
+ type ModifierName<QB extends AnyQueryBuilder> =
1417
+ ChecksModifiers<QB> extends true ? keyof KnownModifiers<QB> & string : string;
1418
+
1419
+ type ModifyArg<QB extends AnyQueryBuilder> =
1420
+ | ModifierName<QB>
1421
+ | ModifierName<QB>[]
1422
+ | ModifierFunction<QB>
1423
+ | Record<string, Expression<PrimitiveValue>>;
1424
+
1425
+ // The related classes are found through the relation mappings, at each step
1426
+ // of the path. P are the names registered with `modifiers()`.
1427
+ type UnknownModifiers<Mappings, T, P> = T extends unknown
1428
+ ? {
1429
+ [K in keyof T & string]: T[K] extends { names: infer N; children: infer Children }
1430
+ ? MappedClass<Mappings, K> extends infer C
1431
+ ? [C] extends [never]
1432
+ ? never
1433
+ : | (ChecksClassModifiers<ClassModifiers<C>> extends true
1434
+ ? Exclude<N, keyof ClassModifiers<C> | P>
1435
+ : never)
1436
+ | UnknownModifiers<ClassMappings<C>, Children, P>
1437
+ : never
1438
+ : never;
1439
+ }[keyof T & string]
1440
+ : never;
1441
+
1442
+ // Mappings with their general type, e.g. inherited from `Model`, give no class.
1443
+ type MappedClass<Mappings, K> = string extends keyof Mappings
1444
+ ? never
1445
+ : K extends keyof Mappings
1446
+ ? Mappings[K] extends { modelClass: infer C }
1447
+ ? C extends abstract new (...args: any) => any
1448
+ ? C
1449
+ : C extends () => infer RC
1450
+ ? RC
1451
+ : never
1452
+ : never
1453
+ : never;
1454
+
1455
+ /**
1456
+ * Parses the modifiers of relation expressions into a tree of the relations
1457
+ * with their modifier names: 'pets(a, b).owner' ->
1458
+ * `{ pets: { names: 'a' | 'b', children: { owner: ... } } }`. It is separate
1459
+ * from `ParseRelationExpression`, so that the narrowing types don't carry the
1460
+ * names.
1461
+ */
1462
+ type ParseModifierExpression<E> = string extends E
1463
+ ? {}
1464
+ : E extends string
1465
+ ? ParseModifierString<E>
1466
+ : ParseModifierObject<E>;
1467
+
1468
+ // A union of the trees of the list items, as they can name the same relation.
1469
+ type ParseModifierList<L extends string[], Acc = never> = L extends [
1470
+ infer H extends string,
1471
+ ...infer T extends string[],
1472
+ ]
1473
+ ? ParseModifierList<T, Acc | ParseModifierString<H>>
1474
+ : Acc;
1475
+
1476
+ type ParseModifierString<S extends string> =
1477
+ Trim<S> extends `[${infer Inner}]`
1478
+ ? ParseModifierList<SplitList<Inner>>
1479
+ : Trim<S> extends `${infer Head}.${infer Rest}`
1480
+ ? IsBalanced<Head> extends true
1481
+ ? ModifierNode<Head, ParseModifierString<Rest>>
1482
+ : {}
1483
+ : ModifierNode<S, {}>;
1484
+
1485
+ type ModifierNode<S extends string, Children> = [RelationName<S>] extends [never]
1486
+ ? {}
1487
+ : { [K in RelationName<S>]: { names: ModifierNames<S>; children: Children } };
1488
+
1489
+ // Empty arguments, e.g. of 'pets()' or a trailing comma, are no names.
1490
+ type ModifierNames<S extends string> =
1491
+ Trim<S> extends `${string}(${infer Args})` ? Exclude<Trim<SplitList<Args>[number]>, ''> : never;
1492
+
1493
+ type ParseModifierObject<E> = string extends keyof E
1494
+ ? {}
1495
+ : {
1496
+ [
1497
+ K in keyof E as K extends `$${string}`
1498
+ ? never
1499
+ : false extends E[K]
1500
+ ? never
1501
+ : E[K] extends { $relation: any }
1502
+ ? never
1503
+ : K
1504
+ ]: {
1505
+ names: E[K] extends { $modify: infer N }
1506
+ ? N extends readonly (infer I extends string)[]
1507
+ ? I
1508
+ : N extends string
1509
+ ? N
1510
+ : never
1511
+ : never;
1512
+ children: E[K] extends object ? ParseModifierObject<E[K]> : {};
1513
+ };
1514
+ };
1515
+
1290
1516
  export interface ForClassMethod {
1291
1517
  <M extends Model>(modelClass: ModelConstructor<M>): QueryBuilderType<M>;
1292
1518
  }
@@ -1301,15 +1527,18 @@ export type JsonObjectOrFieldExpression = object | object[] | FieldExpression;
1301
1527
  export type Selection<QB extends AnyQueryBuilder> = ColumnRef | AnyQueryBuilder | CallbackVoid<QB>;
1302
1528
 
1303
1529
  export interface SelectMethod<out QB extends AnyQueryBuilder> {
1304
- // These must come first so that we get autocomplete.
1305
- <QBP extends QB>(...columns: IfStrict<ModelProps<ModelType<QBP>>, never>[]): QB;
1530
+ // The column overload comes first, for autocompletion, and last in strict
1531
+ // mode, so that errors are reported for it:
1532
+ <QBP extends QB, const C extends readonly [unknown, ...unknown[]]>(
1533
+ ...columns: IfStrict<ModelProps<ModelType<QBP>>[], StrictSelections<ModelType<QBP>, C>>
1534
+ ): IfStrict<QB, SelectQueryBuilder<QBP, C>>;
1306
1535
  <QBP extends QB>(columns: IfStrict<ModelProps<ModelType<QBP>>, never>[]): QB;
1307
1536
 
1308
1537
  <QBP extends QB>(...columns: IfStrict<Selection<QBP>, never>[]): QB;
1309
1538
  <QBP extends QB>(columns: IfStrict<Selection<QBP>, never>[]): QB;
1310
1539
 
1311
- // Allows things like `select(1)`, not sure if we should be more specific here?
1312
- <QBP extends QB>(...args: IfStrict<any, never>[]): QB;
1540
+ // Allow anything else in loose mode, like `select(1)`:
1541
+ <QBP extends QB>(...args: IfStrict<{} | null | undefined, never>[]): QB;
1313
1542
 
1314
1543
  <QBP extends QB, const C extends readonly unknown[] = []>(
1315
1544
  ...columns: StrictSelections<ModelType<QBP>, C>
@@ -1338,7 +1567,7 @@ export interface JsonExtraction {
1338
1567
 
1339
1568
  // Extractions without an alias, or with several, add any properties.
1340
1569
  export interface JsonExtract<out QB extends AnyQueryBuilder> {
1341
- // These must come first so that we get autocomplete.
1570
+ // The column overloads come first, for autocompletion:
1342
1571
  <QBP extends QB, const C extends string = never, const A extends string = string>(
1343
1572
  column: ColumnArg<ModelType<QBP>, C>,
1344
1573
  path: string,
@@ -1359,7 +1588,7 @@ export interface JsonExtract<out QB extends AnyQueryBuilder> {
1359
1588
  }
1360
1589
 
1361
1590
  export interface JsonSet<out QB extends AnyQueryBuilder> {
1362
- // These must come first so that we get autocomplete.
1591
+ // The column overloads come first, for autocompletion:
1363
1592
  <QBP extends QB, const C extends string = never>(
1364
1593
  column: ColumnArg<ModelType<QBP>, C>,
1365
1594
  path: string,
@@ -1371,7 +1600,7 @@ export interface JsonSet<out QB extends AnyQueryBuilder> {
1371
1600
  }
1372
1601
 
1373
1602
  export interface JsonInsert<out QB extends AnyQueryBuilder> {
1374
- // These must come first so that we get autocomplete.
1603
+ // The column overloads come first, for autocompletion:
1375
1604
  <QBP extends QB, const C extends string = never>(
1376
1605
  column: ColumnArg<ModelType<QBP>, C>,
1377
1606
  path: string,
@@ -1383,7 +1612,7 @@ export interface JsonInsert<out QB extends AnyQueryBuilder> {
1383
1612
  }
1384
1613
 
1385
1614
  export interface JsonRemove<out QB extends AnyQueryBuilder> {
1386
- // These must come first so that we get autocomplete.
1615
+ // The column overloads come first, for autocompletion:
1387
1616
  <QBP extends QB, const C extends string = never>(
1388
1617
  column: ColumnArg<ModelType<QBP>, C>,
1389
1618
  path: string,
@@ -1394,6 +1623,17 @@ export interface JsonRemove<out QB extends AnyQueryBuilder> {
1394
1623
  }
1395
1624
 
1396
1625
  export interface WhereMethod<out QB extends AnyQueryBuilder> {
1626
+ // The column overloads come first, for autocompletion:
1627
+ <QBP extends QB, const C extends string = never, const O extends Operator = Operator>(
1628
+ col: ColumnArg<ModelType<QBP>, C>,
1629
+ op: O,
1630
+ expr: OperatorValueArg<ModelType<QBP>, C, O>,
1631
+ ): QB;
1632
+ <QBP extends QB, const C extends string = never>(
1633
+ col: ColumnArg<ModelType<QBP>, C>,
1634
+ expr: ValueArg<ModelType<QBP>, C>,
1635
+ ): QB;
1636
+
1397
1637
  (condition: boolean): QB;
1398
1638
  (cb: CallbackVoid<QB>): QB;
1399
1639
  (raw: Raw): QB;
@@ -1402,7 +1642,7 @@ export interface WhereMethod<out QB extends AnyQueryBuilder> {
1402
1642
  (col: RefArg<QB>, op: Operator, expr: Expression<PrimitiveValue>): QB;
1403
1643
  (col: RefArg<QB>, expr: Expression<PrimitiveValue>): QB;
1404
1644
 
1405
- // The column overloads come last, so that errors are reported for them.
1645
+ // The column overloads come last, so that errors are reported for them:
1406
1646
  <QBP extends QB, const C extends string = never, const O extends Operator = Operator>(
1407
1647
  col: ColumnArg<ModelType<QBP>, C>,
1408
1648
  op: O,
@@ -1414,14 +1654,19 @@ export interface WhereMethod<out QB extends AnyQueryBuilder> {
1414
1654
  ): QB;
1415
1655
 
1416
1656
  <QBP extends QB>(obj: IfStrict<PartialModelProps<ModelType<QBP>>, never>): QB;
1417
- // We must allow any keys in the object. The previous type
1418
- // is kind of useless, but maybe one day vscode and other
1419
- // tools can autocomplete using it.
1657
+ // Allow any keys in loose mode. The model's properties above only serve
1658
+ // autocompletion.
1420
1659
  (obj: IfStrict<object, never>): QB;
1421
1660
  <QBP extends QB, const O extends object = {}>(obj: O & StrictWhereObject<ModelType<QBP>, O>): QB;
1422
1661
  }
1423
1662
 
1424
1663
  export interface WhereLikeMethod<out QB extends AnyQueryBuilder> {
1664
+ // The column overload comes first, for autocompletion, and last, so that
1665
+ // errors are reported for it:
1666
+ <QBP extends QB, const C extends string = never>(
1667
+ col: ColumnArg<ModelType<QBP>, C>,
1668
+ expr: Expression<string>,
1669
+ ): QB;
1425
1670
  (col: RefArg<QB>, expr: Expression<string>): QB;
1426
1671
  <QBP extends QB, const C extends string = never>(
1427
1672
  col: ColumnArg<ModelType<QBP>, C>,
@@ -1442,13 +1687,12 @@ export interface WhereExistsMethod<out QB extends AnyQueryBuilder> {
1442
1687
  }
1443
1688
 
1444
1689
  export interface WhereInMethod<out QB extends AnyQueryBuilder> {
1445
- // These must come first so that we get autocomplete.
1446
- <QBP extends QB>(
1447
- col: IfStrict<ModelProps<ModelType<QBP>>, never>,
1448
- expr: Expression<PrimitiveValue>,
1690
+ // The column overload comes first, for autocompletion, and last in strict
1691
+ // mode, so that errors are reported for it:
1692
+ <QBP extends QB, const C extends string = never>(
1693
+ col: ColumnArg<ModelType<QBP>, C>,
1694
+ expr: IfStrict<Expression<PrimitiveValue> | CallbackVoid<QB>, WhereInValues<QB, QBP, C>>,
1449
1695
  ): QB;
1450
- <QBP extends QB>(col: IfStrict<ModelProps<ModelType<QBP>>, never>, cb: CallbackVoid<QB>): QB;
1451
- <QBP extends QB>(col: IfStrict<ModelProps<ModelType<QBP>>, never>, qb: AnyQueryBuilder): QB;
1452
1696
 
1453
1697
  (col: RefArg<QB> | RefArg<QB>[], expr: readonly Expression<PrimitiveValue>[]): QB;
1454
1698
  (col: RefArg<QB> | RefArg<QB>[], cb: CallbackVoid<QB>): QB;
@@ -1456,7 +1700,7 @@ export interface WhereInMethod<out QB extends AnyQueryBuilder> {
1456
1700
 
1457
1701
  <QBP extends QB, const C extends string = never>(
1458
1702
  col: StrictColumn<ModelType<QBP>, C>,
1459
- expr: readonly ValueArg<ModelType<QBP>, C>[] | Raw | AnyQueryBuilder | CallbackVoid<QB>,
1703
+ expr: WhereInValues<QB, QBP, C>,
1460
1704
  ): QB;
1461
1705
  <QBP extends QB, const C extends readonly unknown[] = []>(
1462
1706
  col: StrictColumns<ModelType<QBP>, C>,
@@ -1464,6 +1708,9 @@ export interface WhereInMethod<out QB extends AnyQueryBuilder> {
1464
1708
  ): QB;
1465
1709
  }
1466
1710
 
1711
+ type WhereInValues<QB extends AnyQueryBuilder, QBP extends QB, C extends string> =
1712
+ readonly ValueArg<ModelType<QBP>, C>[] | Raw | AnyQueryBuilder | CallbackVoid<QB>;
1713
+
1467
1714
  export interface WhereBetweenMethod<out QB extends AnyQueryBuilder> {
1468
1715
  (column: RefArg<QB>, range: [Expression<PrimitiveValue>, Expression<PrimitiveValue>]): QB;
1469
1716
  <QBP extends QB, const C extends string = never>(
@@ -1478,7 +1725,7 @@ export interface WhereNullMethod<out QB extends AnyQueryBuilder> {
1478
1725
  }
1479
1726
 
1480
1727
  export interface WhereColumnMethod<out QB extends AnyQueryBuilder> {
1481
- // These must come first so that we get autocomplete.
1728
+ // The column overloads come first, for autocompletion:
1482
1729
  <QBP extends QB, const C extends string = never, const D extends string = never>(
1483
1730
  col1: ColumnArg<ModelType<QBP>, C>,
1484
1731
  op: Operator,
@@ -1501,7 +1748,7 @@ export interface WhereColumnMethod<out QB extends AnyQueryBuilder> {
1501
1748
  }
1502
1749
 
1503
1750
  export interface WhereJsonObject<out QB extends AnyQueryBuilder> {
1504
- // These must come first so that we get autocomplete.
1751
+ // The column overloads come first, for autocompletion:
1505
1752
  <QBP extends QB, const C extends string = never>(
1506
1753
  col: ColumnArg<ModelType<QBP>, C>,
1507
1754
  value: any,
@@ -1511,7 +1758,7 @@ export interface WhereJsonObject<out QB extends AnyQueryBuilder> {
1511
1758
  }
1512
1759
 
1513
1760
  export interface WhereJsonPath<out QB extends AnyQueryBuilder> {
1514
- // These must come first so that we get autocomplete.
1761
+ // The column overloads come first, for autocompletion:
1515
1762
  <QBP extends QB, const C extends string = never>(
1516
1763
  col: ColumnArg<ModelType<QBP>, C>,
1517
1764
  jsonPath: string,
@@ -1664,7 +1911,7 @@ export interface JoinRelatedOptions {
1664
1911
 
1665
1912
  export interface JoinRelatedMethod<out QB extends AnyQueryBuilder> {
1666
1913
  <QBP extends QB, const E extends RelationExpression<ModelType<QBP>> = never>(
1667
- expr: E & CheckRelationExpression<ModelType<QBP>, E>,
1914
+ expr: E & CheckRelationExpression<ModelType<QBP>, E, QBP>,
1668
1915
  opt?: JoinRelatedOptions,
1669
1916
  ): QB;
1670
1917
  }
@@ -1868,7 +2115,7 @@ type WriteQueryBuilder<QB extends AnyQueryBuilder, M extends Model, R> = [R] ext
1868
2115
  * The writes after `returning()` on single and maybe-single query builders,
1869
2116
  * which resolve to one row.
1870
2117
  */
1871
- interface ReturningWrites<QB extends AnyQueryBuilder, M extends Model, W> {
2118
+ export interface ReturningWrites<QB extends AnyQueryBuilder, M extends Model, W> {
1872
2119
  update(update: PartialModelRow<M>): Rebind<QB, M, W>;
1873
2120
  update(): Rebind<QB, M, W>;
1874
2121
  patch(update: PartialModelPatch<M>): Rebind<QB, M, W>;
@@ -1993,7 +2240,8 @@ export interface GraphOptions {
1993
2240
  maxBatchSize?: number;
1994
2241
  /**
1995
2242
  * Apply `limit()`, `offset()` and `first()` in graph modifiers to the related
1996
- * items of each parent. Defaults to `true`. Only applies to `withGraphFetched()`.
2243
+ * items of each parent. Defaults to `true`. Only applies to
2244
+ * `withGraphFetched()`.
1997
2245
  */
1998
2246
  limitPerParent?: boolean;
1999
2247
  }
@@ -2033,11 +2281,24 @@ export interface ClearContextMethod<out QB extends AnyQueryBuilder> {
2033
2281
  }
2034
2282
 
2035
2283
  export interface ModifyMethod<out QB extends AnyQueryBuilder> {
2036
- (modifier: Modifier<QB> | Modifier<QB>[], ...args: any[]): QB;
2284
+ (modifier: ModifyArg<QB> | ModifyArg<QB>[], ...args: any[]): QB;
2037
2285
  }
2038
2286
 
2039
- export interface ModifiersMethod<out QB extends AnyQueryBuilder> {
2040
- (modifiers: Modifiers): QB;
2287
+ // Registered modifiers add up, on queries with and without a model class.
2288
+ type RegisterModifiers<X, O> =
2289
+ X extends QueryExtra<infer C, infer P, infer I>
2290
+ ? QueryExtra<C, P & O, I>
2291
+ : QueryExtra<unknown, O>;
2292
+
2293
+ export interface ModifiersMethod<
2294
+ out QB extends AnyQueryBuilder,
2295
+ M extends Model = ModelType<QB>,
2296
+ R = ResultType<QB>,
2297
+ X = ExtraType<QB>,
2298
+ > {
2299
+ <const O extends Modifiers>(
2300
+ modifiers: O,
2301
+ ): IfStrict<QB, Rebind<QB, M, R, RegisterModifiers<X, O>>>;
2041
2302
  (): QB;
2042
2303
  }
2043
2304
 
@@ -2051,7 +2312,11 @@ export interface CatchablePromiseLike<R> extends PromiseLike<R> {
2051
2312
  ): Promise<R | FR>;
2052
2313
  }
2053
2314
 
2054
- export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseLike<R> {
2315
+ export class QueryBuilder<
2316
+ M extends Model,
2317
+ R = M[],
2318
+ X = unknown,
2319
+ > implements CatchablePromiseLike<R> {
2055
2320
  static forClass: ForClassMethod;
2056
2321
 
2057
2322
  constructor(modelClass: ModelConstructor<M>);
@@ -2240,35 +2505,35 @@ export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseL
2240
2505
  groupBy: GroupByMethod<this>;
2241
2506
  groupByRaw: RawInterface<this>;
2242
2507
 
2243
- findById(id: MaybeCompositeId): MaybeSingleQueryBuilder<this>;
2244
- findByIds(ids: MaybeCompositeId[]): this;
2508
+ findById(id: IdArg<this>): MaybeSingleQueryBuilder<this>;
2509
+ findByIds(ids: IdArg<this>[]): this;
2245
2510
  findOne: WhereMethod<MaybeSingleQueryBuilder<this>>;
2246
2511
 
2247
2512
  execute(): Promise<R>;
2248
2513
  castTo<MC extends Model>(modelClass: ModelConstructor<MC>): QueryBuilderType<MC>;
2249
- castTo<R>(): QueryBuilder<M, R>;
2514
+ castTo<R>(): QueryBuilder<M, R, X>;
2250
2515
 
2251
2516
  update(update: PartialModelRow<M>): NumberQueryBuilder<this>;
2252
2517
  update(): NumberQueryBuilder<this>;
2253
2518
  updateById(
2254
- id: MaybeCompositeId,
2519
+ id: IdArg<this>,
2255
2520
  update: PartialModelRow<M>,
2256
2521
  ): NumberQueryBuilder<MaybeSingleQueryBuilder<this>>;
2257
2522
  updateAndFetch(update: PartialModelRow<M>): SingleQueryBuilder<this>;
2258
- updateAndFetchById(id: MaybeCompositeId, update: PartialModelRow<M>): SingleQueryBuilder<this>;
2523
+ updateAndFetchById(id: IdArg<this>, update: PartialModelRow<M>): SingleQueryBuilder<this>;
2259
2524
 
2260
2525
  patch(update: PartialModelPatch<M>): NumberQueryBuilder<this>;
2261
2526
  patch(): NumberQueryBuilder<this>;
2262
2527
  patchById(
2263
- id: MaybeCompositeId,
2528
+ id: IdArg<this>,
2264
2529
  update: PartialModelPatch<M>,
2265
2530
  ): NumberQueryBuilder<MaybeSingleQueryBuilder<this>>;
2266
2531
  patchAndFetch(update: PartialModelPatch<M>): SingleQueryBuilder<this>;
2267
- patchAndFetchById(id: MaybeCompositeId, update: PartialModelPatch<M>): SingleQueryBuilder<this>;
2532
+ patchAndFetchById(id: IdArg<this>, update: PartialModelPatch<M>): SingleQueryBuilder<this>;
2268
2533
 
2269
2534
  del(): NumberQueryBuilder<this>;
2270
2535
  delete(): NumberQueryBuilder<this>;
2271
- deleteById(id: MaybeCompositeId): NumberQueryBuilder<MaybeSingleQueryBuilder<this>>;
2536
+ deleteById(id: IdArg<this>): NumberQueryBuilder<MaybeSingleQueryBuilder<this>>;
2272
2537
 
2273
2538
  insert(insert: PartialModelRow<M>): SingleQueryBuilder<this>;
2274
2539
  insert(insert: PartialModelRow<M>[]): ArrayQueryBuilder<this>;
@@ -2292,15 +2557,15 @@ export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseL
2292
2557
  // With literal relation expressions, the fetched relations become required
2293
2558
  // on the result type, see WithGraphQueryBuilder.
2294
2559
  withGraphFetched<const E extends RelationExpression<M>>(
2295
- expr: E & CheckRelationExpression<M, E>,
2560
+ expr: E & CheckRelationExpression<M, E, this>,
2296
2561
  options?: GraphOptions,
2297
2562
  ): WithGraphQueryBuilder<this, E>;
2298
2563
  withGraphJoined<const E extends RelationExpression<M>>(
2299
- expr: E & CheckRelationExpression<M, E>,
2564
+ expr: E & CheckRelationExpression<M, E, this>,
2300
2565
  options?: GraphOptions,
2301
2566
  ): WithGraphQueryBuilder<this, E>;
2302
2567
  withGraph<const E extends RelationExpression<M>>(
2303
- expr: E & CheckRelationExpression<M, E>,
2568
+ expr: E & CheckRelationExpression<M, E, this>,
2304
2569
  options?: WithGraphOptions,
2305
2570
  ): WithGraphQueryBuilder<this, E>;
2306
2571
 
@@ -2378,7 +2643,7 @@ export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseL
2378
2643
  clearContext: ClearContextMethod<this>;
2379
2644
 
2380
2645
  modify: ModifyMethod<this>;
2381
- modifiers: ModifiersMethod<this>;
2646
+ modifiers: ModifiersMethod<this, M, R, X>;
2382
2647
 
2383
2648
  isFind: BooleanReturningMethod;
2384
2649
  isExecutable: BooleanReturningMethod;
@@ -2404,27 +2669,29 @@ export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseL
2404
2669
 
2405
2670
  ModelType: M;
2406
2671
  ResultType: R;
2672
+ ExtraType: X;
2407
2673
 
2408
- // The model and result types that `RebindType` is instantiated with. They
2409
- // are `any` where `this` isn't rebound, e.g. when TypeScript checks the
2410
- // members of a custom query builder against this class.
2674
+ // The model, result and extra types that `RebindType` is instantiated
2675
+ // with. They are `any` where `this` isn't rebound, e.g. when TypeScript
2676
+ // checks the members of a custom query builder against this class.
2411
2677
  '~rebind': unknown;
2412
- '~M': this['~rebind'] extends [any, any] ? this['~rebind'][0] : any;
2413
- '~R': this['~rebind'] extends [any, any] ? this['~rebind'][1] : any;
2678
+ '~M': this['~rebind'] extends [any, any, any] ? this['~rebind'][0] : any;
2679
+ '~R': this['~rebind'] extends [any, any, any] ? this['~rebind'][1] : any;
2680
+ '~X': this['~rebind'] extends [any, any, any] ? this['~rebind'][2] : any;
2414
2681
 
2415
2682
  /**
2416
- * This query builder, re-instantiated with other model and result types,
2417
- * e.g. by `first()` or `page()`. Custom query builders redeclare it to keep
2418
- * their own type:
2683
+ * This query builder, re-instantiated with other model, result and extra
2684
+ * types, e.g. by `first()` or `page()`. Custom query builders redeclare it
2685
+ * to keep their own type:
2419
2686
  *
2420
- * declare RebindType: MyQueryBuilder<this['~M'], this['~R']>;
2687
+ * declare RebindType: MyQueryBuilder<this['~M'], this['~R'], this['~X']>;
2421
2688
  */
2422
- RebindType: QueryBuilder<this['~M'], this['~R']>;
2689
+ RebindType: QueryBuilder<this['~M'], this['~R'], this['~X']>;
2423
2690
 
2424
2691
  // The query builder rebound to the result kinds of the helper types, e.g.
2425
2692
  // `SingleQueryBuilder`. As members, they also resolve where `this` is
2426
2693
  // generic, e.g. in the methods of custom query builders. Of the members
2427
- // starting with `~`, only `~M` and `~R` are API, for `RebindType`.
2694
+ // starting with `~`, only `~M`, `~R` and `~X` are API, for `RebindType`.
2428
2695
  '~single': Rebind<this, M, ResultItem<R, M>>;
2429
2696
  '~maybeSingle': Rebind<this, M, ResultItem<R, M> | undefined>;
2430
2697
  '~array': Rebind<this, M, ResultItem<R, M>[]>;
@@ -2441,8 +2708,6 @@ export class QueryBuilder<M extends Model, R = M[]> implements CatchablePromiseL
2441
2708
  ): Promise<R | FR>;
2442
2709
  }
2443
2710
 
2444
- export type X<T> = Promise<T>;
2445
-
2446
2711
  export interface FetchGraphOptions {
2447
2712
  transaction?: TransactionOrKnex;
2448
2713
  skipFetched?: boolean;
@@ -2790,7 +3055,8 @@ export interface ConstructorFunctionType<T = any> extends PrototypeType<T> {
2790
3055
  new (...args: any[]): T;
2791
3056
  }
2792
3057
 
2793
- // for internal use on generic static this deduction, copied from https://github.com/microsoft/TypeScript/issues/5863#issuecomment-1483978415
3058
+ // Used internally to infer the generic static `this`, copied from:
3059
+ // https://github.com/microsoft/TypeScript/issues/5863#issuecomment-1483978415
2794
3060
  export type ConstructorType<T = unknown, Static extends Record<string, any> = PrototypeType<T>> = (
2795
3061
  ConstructorFunctionType<T> | PrototypeType<T>
2796
3062
  ) & {
@@ -2808,7 +3074,7 @@ export interface ModelClass<M extends Model> extends ModelConstructor<M> {
2808
3074
  relationMappings: RelationMappings | RelationMappingsThunk;
2809
3075
  modelPaths: string[];
2810
3076
  jsonAttributes: string[] | readonly string[];
2811
- virtualAttributes: string[];
3077
+ virtualAttributes: readonly string[];
2812
3078
  uidProp: string;
2813
3079
  uidRefProp: string;
2814
3080
  dbRefProp: string;
@@ -2867,7 +3133,7 @@ export interface ModelClass<M extends Model> extends ModelConstructor<M> {
2867
3133
 
2868
3134
  transaction<T>(callback: (trx: Transaction) => Promise<T>): Promise<T>;
2869
3135
  transaction<T>(
2870
- trxOrKnex: TransactionOrKnex,
3136
+ trxOrKnex: TransactionOrKnex | undefined,
2871
3137
  callback: (trx: Transaction) => Promise<T>,
2872
3138
  ): Promise<T>;
2873
3139
 
@@ -2921,7 +3187,7 @@ export class Model {
2921
3187
  static relationMappings: RelationMappings | RelationMappingsThunk;
2922
3188
  static modelPaths: string[];
2923
3189
  static jsonAttributes: string[] | readonly string[];
2924
- static virtualAttributes: string[];
3190
+ static virtualAttributes: readonly string[];
2925
3191
  static uidProp: string;
2926
3192
  static uidRefProp: string;
2927
3193
  static dbRefProp: string;
@@ -2949,16 +3215,28 @@ export class Model {
2949
3215
 
2950
3216
  static defaultGraphOptions?: GraphOptions;
2951
3217
 
2952
- static query<M extends Model>(
2953
- this: ConstructorType<M>,
3218
+ // The id column K is inferred separately from the class C, so that it
3219
+ // resolves through the constraint of a generic class, e.g. in static helpers
3220
+ // with `this: T`.
3221
+ static query<M extends Model, C = unknown, K = unknown>(
3222
+ this: ConstructorType<M> & IfStrict<unknown, C & { idColumn?: K }>,
2954
3223
  trxOrKnex?: TransactionOrKnex,
2955
- ): QueryBuilderType<M>;
3224
+ ): IfStrict<
3225
+ QueryBuilderType<M>,
3226
+ WithExtra<QueryBuilderType<M>, QueryExtra<C, {}, ColumnsId<K, M>>>
3227
+ >;
2956
3228
 
2957
- static relatedQuery<M extends Model, K extends keyof M>(
2958
- this: ConstructorType<M>,
3229
+ static relatedQuery<M extends Model, K extends keyof M, C = unknown>(
3230
+ this: ConstructorType<M> & IfStrict<unknown, C>,
2959
3231
  relationName: K,
2960
3232
  trxOrKnex?: TransactionOrKnex,
2961
- ): ArrayRelatedQueryBuilder<M[K]>;
3233
+ ): IfStrict<
3234
+ ArrayRelatedQueryBuilder<M[K]>,
3235
+ WithExtra<
3236
+ ArrayRelatedQueryBuilder<M[K]>,
3237
+ QueryExtra<MappedClass<ClassMappings<C>, K>, {}, ClassId<MappedClass<ClassMappings<C>, K>>>
3238
+ >
3239
+ >;
2962
3240
 
2963
3241
  static relatedQuery<RM extends Model>(
2964
3242
  relationName: string,
@@ -3215,32 +3493,26 @@ export interface initialize {
3215
3493
 
3216
3494
  /**
3217
3495
  * The type of the values that a JSON schema declared with `as const`
3218
- * describes, e.g. the properties of a model from its `jsonSchema`:
3496
+ * describes, e.g. of JSON columns, or of models that subclasses extend, whose
3497
+ * `jsonSchema` has the general type for `FromModel`:
3219
3498
  *
3220
- * class Person extends Model {
3221
- * static jsonSchema = {
3222
- * type: 'object',
3223
- * required: ['firstName'],
3224
- * properties: {
3225
- * id: { type: 'integer' },
3226
- * firstName: { type: 'string' },
3227
- * age: { type: ['integer', 'null'] },
3228
- * },
3229
- * } as const;
3499
+ * const entrySchema = { type: 'object', properties: { ... } } as const;
3230
3500
  *
3231
- * pets?: Animal[];
3501
+ * class Entry extends Model {
3502
+ * static jsonSchema: ReadonlyJSONSchema = entrySchema;
3232
3503
  * }
3233
3504
  *
3234
- * interface Person extends FromSchema<typeof Person.jsonSchema> {}
3505
+ * interface Entry extends FromJSONSchema<typeof entrySchema>, FromModel<typeof Entry> {}
3235
3506
  *
3236
3507
  * Supports `type` (also as an array), `required`, `nullable`, `enum`,
3237
3508
  * `const`, `items` (also as a tuple), nested `properties`, `anyOf` and
3238
- * `oneOf`. Formats like 'date-time' stay strings, as in JSON. Properties
3239
- * that aren't required are optional. Next to `type` or `properties`, `anyOf`
3240
- * and `oneOf` only add constraints, e.g. alternative `required` lists, and
3241
- * don't change the type.
3509
+ * `oneOf`. Formats like 'date-time' stay strings, as in JSON, see the
3510
+ * timestamps recipe for the dates that drivers return. Properties that aren't
3511
+ * required are optional. Next to `type` or `properties`, `anyOf` and `oneOf`
3512
+ * only add constraints, e.g. alternative `required` lists, and don't change
3513
+ * the type.
3242
3514
  */
3243
- export type FromSchema<S> = S extends boolean
3515
+ export type FromJSONSchema<S> = S extends boolean
3244
3516
  ? unknown
3245
3517
  : S extends { const: infer C }
3246
3518
  ? Extract<C, SchemaType<S>>
@@ -3251,7 +3523,7 @@ export type FromSchema<S> = S extends boolean
3251
3523
  : S extends { properties: object }
3252
3524
  ? SchemaObject<S>
3253
3525
  : S extends { anyOf: readonly (infer A)[] } | { oneOf: readonly (infer A)[] }
3254
- ? FromSchema<A> | SchemaNullable<S>
3526
+ ? FromJSONSchema<A> | SchemaNullable<S>
3255
3527
  : unknown;
3256
3528
 
3257
3529
  // The type that `type` allows, which `enum` and `const` values are narrowed
@@ -3280,7 +3552,7 @@ type SchemaTypeName<N, S> = N extends 'string'
3280
3552
  type SchemaArray<S> = S extends { items: infer I }
3281
3553
  ? I extends readonly unknown[]
3282
3554
  ? SchemaTuple<I, S>
3283
- : FromSchema<I>[]
3555
+ : FromJSONSchema<I>[]
3284
3556
  : unknown[];
3285
3557
 
3286
3558
  // Tuple `items` only check the items that are present: they are required
@@ -3288,12 +3560,12 @@ type SchemaArray<S> = S extends { items: infer I }
3288
3560
  // `additionalItems` restricts them.
3289
3561
  type SchemaTuple<I extends readonly unknown[], S> = [
3290
3562
  ...(S extends { minItems: I['length'] }
3291
- ? { -readonly [K in keyof I]: FromSchema<I[K]> }
3292
- : { -readonly [K in keyof I]?: FromSchema<I[K]> }),
3563
+ ? { -readonly [K in keyof I]: FromJSONSchema<I[K]> }
3564
+ : { -readonly [K in keyof I]?: FromJSONSchema<I[K]> }),
3293
3565
  ...(S extends { additionalItems: false }
3294
3566
  ? []
3295
3567
  : S extends { additionalItems: infer A }
3296
- ? FromSchema<A>[]
3568
+ ? FromJSONSchema<A>[]
3297
3569
  : unknown[]),
3298
3570
  ];
3299
3571
 
@@ -3305,16 +3577,97 @@ type SchemaObject<S> = S extends { properties: infer P }
3305
3577
 
3306
3578
  // The intersection is flattened into one object type for readable hovers.
3307
3579
  type SchemaProperties<P, R> = {
3308
- -readonly [K in keyof P as K extends R ? K : never]: FromSchema<P[K]>;
3580
+ -readonly [K in keyof P as K extends R ? K : never]: FromJSONSchema<P[K]>;
3309
3581
  } & {
3310
- -readonly [K in keyof P as K extends R ? never : K]?: FromSchema<P[K]>;
3582
+ -readonly [K in keyof P as K extends R ? never : K]?: FromJSONSchema<P[K]>;
3311
3583
  } extends infer O
3312
3584
  ? { [K in keyof O]: O[K] }
3313
3585
  : never;
3314
3586
 
3587
+ /**
3588
+ * The properties of a model class, derived from its `jsonSchema` declared
3589
+ * `as const` and its `relationMappings`:
3590
+ *
3591
+ * class Person extends Model {
3592
+ * static jsonSchema = { ... } as const;
3593
+ *
3594
+ * static relationMappings = () => ({
3595
+ * parent: {
3596
+ * relation: Model.BelongsToOneRelation,
3597
+ * modelClass: Person,
3598
+ * join: { from: 'persons.parentId', to: 'persons.id' },
3599
+ * },
3600
+ * });
3601
+ * }
3602
+ *
3603
+ * interface Person extends FromModel<typeof Person> {}
3604
+ *
3605
+ * The properties are typed as with `FromJSONSchema`. Relations are optional, as
3606
+ * they are only present when fetched: to-one relations are typed `M | null`,
3607
+ * as `withGraphFetched()` sets missing ones to `null`, and to-many relations
3608
+ * `M[]`. Relations with a module path as `modelClass`, and mappings declared
3609
+ * as `RelationMapping` instead of inferred, are left out, as are a schema and
3610
+ * mappings with their general types, e.g. inherited from `Model`. The names of
3611
+ * `virtualAttributes` declared `as const` are kept for `ModelJson`.
3612
+ */
3613
+ export type FromModel<C extends { jsonSchema: unknown; relationMappings: unknown }> =
3614
+ ModelSchemaProperties<C['jsonSchema']> &
3615
+ ModelRelationProperties<C['relationMappings']> &
3616
+ VirtualAttributeKeys<C>;
3617
+
3618
+ // One key per name, so that subclasses that add virtual attributes have the
3619
+ // same keys as their parent for the inherited ones.
3620
+ type VirtualAttributeKeys<C> = C extends { virtualAttributes: infer V extends readonly string[] }
3621
+ ? string extends V[number]
3622
+ ? {}
3623
+ : { readonly [K in V[number] as `~virtual:${K}`]?: true }
3624
+ : {};
3625
+
3626
+ type ModelSchemaProperties<S> = ReadonlyJSONSchema extends S
3627
+ ? {}
3628
+ : FromJSONSchema<S> extends infer P extends object
3629
+ ? P
3630
+ : {};
3631
+
3632
+ // Relations are picked by their mappings, as resolving the related models
3633
+ // while the model's own type is built would make it its own base type.
3634
+ type ModelRelationProperties<T, R = T extends () => infer M ? M : T> = string extends keyof R
3635
+ ? {}
3636
+ : {
3637
+ -readonly [K in keyof R as IsMappedRelation<R[K]> extends true ? K : never]?: MappedRelation<
3638
+ R[K]
3639
+ >;
3640
+ };
3641
+
3642
+ // Relation types without a brand and module paths can't be resolved.
3643
+ type IsMappedRelation<M> = M extends { relation: infer T; modelClass: infer C }
3644
+ ? [MappedKind<T>] extends [never]
3645
+ ? false
3646
+ : C extends string
3647
+ ? false
3648
+ : true
3649
+ : false;
3650
+
3651
+ // The related model is inferred from the constructor without checking it
3652
+ // against `Model`, as that check would circle back through the relations of
3653
+ // self-referencing models.
3654
+ type MappedRelation<M> = M extends { relation: infer T; modelClass: infer C }
3655
+ ? MappedKind<T> extends 'HasMany' | 'ManyToMany' | 'BelongsToMany'
3656
+ ? MappedModel<C>[]
3657
+ : MappedModel<C> | null
3658
+ : never;
3659
+
3660
+ type MappedKind<T> = T extends { readonly [relationKind]?: infer K } ? K : never;
3661
+
3662
+ type MappedModel<C> = C extends abstract new (...args: any) => infer I
3663
+ ? I
3664
+ : C extends () => abstract new (...args: any) => infer I
3665
+ ? I
3666
+ : never;
3667
+
3315
3668
  /**
3316
3669
  * A JSON schema with readonly arrays, the type of `static jsonSchema`, so that
3317
- * it takes schemas declared `as const`, for `FromSchema`. A `JSONSchema` is
3670
+ * it takes schemas declared `as const`, for `FromJSONSchema`. A `JSONSchema` is
3318
3671
  * assignable to it.
3319
3672
  */
3320
3673
  export type ReadonlyJSONSchema = ReadonlyDeep<JSONSchema>;