diff --git a/deno.check.d.ts b/deno.check.d.ts index 97139b6cd..ef9b573cd 100644 --- a/deno.check.d.ts +++ b/deno.check.d.ts @@ -64,6 +64,11 @@ export type PetUpdate = Updateable export type Species = 'dog' | 'cat' declare global { + // @ts-ignore + export class Buffer { + static isBuffer(obj: unknown): obj is { length: number } + static compare(a: Buffer, b: Buffer): number + } export const db: Kysely export function functionThatExpectsPersonWithNonNullValue( person: Person & { nullable_column: string }, diff --git a/src/schema/alter-table-add-index-builder.ts b/src/schema/alter-table-add-index-builder.ts index 6110883a0..f7d3b4b7a 100644 --- a/src/schema/alter-table-add-index-builder.ts +++ b/src/schema/alter-table-add-index-builder.ts @@ -26,6 +26,23 @@ export class AlterTableAddIndexBuilder /** * Makes the index unique. + * + * ### Examples + * + * ```ts + * await db.schema + * .alterTable('person') + * .addIndex('person_first_name_index') + * .unique() + * .column('email') + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * alter table `person` add unique index `person_first_name_index` (`email`) + * ``` */ unique(): AlterTableAddIndexBuilder { return new AlterTableAddIndexBuilder({ @@ -48,11 +65,11 @@ export class AlterTableAddIndexBuilder * * ```ts * await db.schema - * .alterTable('person') - * .createIndex('person_first_name_and_age_index') - * .column('first_name') - * .column('age desc') - * .execute() + * .alterTable('person') + * .addIndex('person_first_name_and_age_index') + * .column('first_name') + * .column('age desc') + * .execute() * ``` * * The generated SQL (MySQL): @@ -84,10 +101,10 @@ export class AlterTableAddIndexBuilder * * ```ts * await db.schema - * .alterTable('person') - * .addIndex('person_first_name_and_age_index') - * .columns(['first_name', 'age desc']) - * .execute() + * .alterTable('person') + * .addIndex('person_first_name_and_age_index') + * .columns(['first_name', 'age desc']) + * .execute() * ``` * * The generated SQL (MySQL): @@ -144,6 +161,23 @@ export class AlterTableAddIndexBuilder /** * Specifies the index type. + * + * ### Examples + * + * ```ts + * await db.schema + * .alterTable('person') + * .addIndex('person_first_name_index') + * .column('first_name') + * .using('hash') + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * alter table `person` add index `person_first_name_index` (`first_name`) using hash + * ``` */ using(indexType: IndexType): AlterTableAddIndexBuilder using(indexType: string): AlterTableAddIndexBuilder diff --git a/src/schema/column-definition-builder.ts b/src/schema/column-definition-builder.ts index 817c07ee9..1935f00f4 100644 --- a/src/schema/column-definition-builder.ts +++ b/src/schema/column-definition-builder.ts @@ -30,6 +30,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * * Some dialects like PostgreSQL don't support this. On PostgreSQL * you can use the `serial` or `bigserial` data type instead. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.autoIncrement().primaryKey()) + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `id` integer primary key auto_increment + * ) + * ``` */ autoIncrement(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -43,6 +60,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * This only works on some dialects like MS SQL Server (MSSQL). * * For PostgreSQL's `generated always as identity` use {@link generatedAlwaysAsIdentity}. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.identity().primaryKey()) + * .execute() + * ``` + * + * The generated SQL (MSSQL): + * + * ```sql + * create table "person" ( + * "id" integer identity primary key + * ) + * ``` */ identity(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -55,6 +89,22 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * * If you want to specify a composite primary key use the * {@link CreateTableBuilder.addPrimaryKeyConstraint} method. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.primaryKey()) + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `id` integer primary key + * ) */ primaryKey(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -72,7 +122,18 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * col.references('person.id') + * await db.schema + * .createTable('pet') + * .addColumn('owner_id', 'integer', (col) => col.references('person.id')) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * create table "pet" ( + * "owner_id" integer references "person" ("id") + * ) * ``` */ references(ref: string): ColumnDefinitionBuilder { @@ -103,7 +164,22 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * col.references('person.id').onDelete('cascade') + * await db.schema + * .createTable('pet') + * .addColumn( + * 'owner_id', + * 'integer', + * (col) => col.references('person.id').onDelete('cascade') + * ) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * create table "pet" ( + * "owner_id" integer references "person" ("id") on delete cascade + * ) * ``` */ onDelete(onDelete: OnModifyForeignAction): ColumnDefinitionBuilder { @@ -124,10 +200,29 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { /** * Adds an `on update` constraint for the foreign key column. * + * If your database engine doesn't support foreign key constraints in the + * column definition (like MySQL 5) you need to call the table level + * {@link CreateTableBuilder.addForeignKeyConstraint} method instead. + * * ### Examples * * ```ts - * col.references('person.id').onUpdate('cascade') + * await db.schema + * .createTable('pet') + * .addColumn( + * 'owner_id', + * 'integer', + * (col) => col.references('person.id').onUpdate('cascade') + * ) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * create table "pet" ( + * "owner_id" integer references "person" ("id") on update cascade + * ) * ``` */ onUpdate(onUpdate: OnModifyForeignAction): ColumnDefinitionBuilder { @@ -147,6 +242,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { /** * Adds a unique constraint for the column. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('email', 'varchar(255)', col => col.unique()) + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `email` varchar(255) unique + * ) + * ``` */ unique(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -156,6 +268,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { /** * Adds a `not null` constraint for the column. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('first_name', 'varchar(255)', col => col.notNull()) + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `first_name` varchar(255) not null + * ) + * ``` */ notNull(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -167,6 +296,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * Adds a `unsigned` modifier for the column. * * This only works on some dialects like MySQL. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('age', 'integer', col => col.unsigned()) + * .execute() + * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `age` integer unsigned + * ) + * ``` */ unsigned(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -180,27 +326,43 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * db.schema + * await db.schema * .createTable('pet') * .addColumn('number_of_legs', 'integer', (col) => col.defaultTo(4)) * .execute() * ``` * + * The generated SQL (MySQL): + * + * ```sql + * create table `pet` ( + * `number_of_legs` integer default 4 + * ) + * ``` + * * Values passed to `defaultTo` are interpreted as value literals by default. You can define * an arbitrary SQL expression using the {@link sql} template tag: * * ```ts * import { sql } from 'kysely' * - * db.schema + * await db.schema * .createTable('pet') * .addColumn( - * 'number_of_legs', - * 'integer', - * (col) => col.defaultTo(sql`any SQL here`) + * 'created_at', + * 'timestamp', + * (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`) * ) * .execute() * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `pet` ( + * `created_at` timestamp default CURRENT_TIMESTAMP + * ) + * ``` */ defaultTo(value: DefaultValueExpression): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -218,13 +380,21 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ```ts * import { sql } from 'kysely' * - * db.schema + * await db.schema * .createTable('pet') * .addColumn('number_of_legs', 'integer', (col) => * col.check(sql`number_of_legs < 5`) * ) * .execute() * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `pet` ( + * `number_of_legs` integer check (number_of_legs < 5) + * ) + * ``` */ check(expression: Expression): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -242,13 +412,21 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ```ts * import { sql } from 'kysely' * - * db.schema + * await db.schema * .createTable('person') * .addColumn('full_name', 'varchar(255)', * (col) => col.generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`) * ) * .execute() * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `full_name` varchar(255) generated always as (concat(first_name, ' ', last_name)) + * ) + * ``` */ generatedAlwaysAs(expression: Expression): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -266,6 +444,23 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * This only works on some dialects like PostgreSQL. * * For MS SQL Server (MSSQL)'s identity column use {@link identity}. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.generatedAlwaysAsIdentity().primaryKey()) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * create table "person" ( + * "id" integer generated always as identity primary key + * ) + * ``` */ generatedAlwaysAsIdentity(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -277,6 +472,27 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { /** * Adds the `generated by default as identity` specifier on supported dialects. + * + * This only works on some dialects like PostgreSQL. + * + * For MS SQL Server (MSSQL)'s identity column use {@link identity}. + * + * ### Examples + * + * ```ts + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.generatedByDefaultAsIdentity().primaryKey()) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * create table "person" ( + * "id" integer generated by default as identity primary key + * ) + * ``` */ generatedByDefaultAsIdentity(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -293,7 +509,9 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * db.schema + * import { sql } from 'kysely' + * + * await db.schema * .createTable('person') * .addColumn('full_name', 'varchar(255)', (col) => col * .generatedAlwaysAs(sql`concat(first_name, ' ', last_name)`) @@ -301,6 +519,14 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ) * .execute() * ``` + * + * The generated SQL (MySQL): + * + * ```sql + * create table `person` ( + * `full_name` varchar(255) generated always as (concat(first_name, ' ', last_name)) stored + * ) + * ``` */ stored(): ColumnDefinitionBuilder { if (!this.#node.generated) { @@ -322,10 +548,17 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * db.schema.createTable('person') - * .addColumn('id', 'integer', col => col.primaryKey()) - * .addColumn('first_name', 'varchar(36)', col => col.modifyFront(sql`collate utf8mb4_general_ci`).notNull()) - * .execute() + * import { sql } from 'kysely' + * + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.primaryKey()) + * .addColumn( + * 'first_name', + * 'varchar(36)', + * (col) => col.modifyFront(sql`collate utf8mb4_general_ci`).notNull() + * ) + * .execute() * ``` * * The generated SQL (MySQL): @@ -355,10 +588,11 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * db.schema.createTable('person') - * .addColumn('id', 'integer', col => col.primaryKey()) - * .addColumn('first_name', 'varchar(30)', col => col.unique().nullsNotDistinct()) - * .execute() + * db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.primaryKey()) + * .addColumn('first_name', 'varchar(30)', col => col.unique().nullsNotDistinct()) + * .execute() * ``` * * The generated SQL (PostgreSQL): @@ -377,8 +611,22 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { } /** - * Adds `if not exists` specifier. - * This only works for PostgreSQL. + * Adds `if not exists` specifier. This only works for PostgreSQL. + * + * ### Examples + * + * ```ts + * await db.schema + * .alterTable('person') + * .addColumn('email', 'varchar(255)', col => col.unique().ifNotExists()) + * .execute() + * ``` + * + * The generated SQL (PostgreSQL): + * + * ```sql + * alter table "person" add column if not exists "email" varchar(255) unique + * ``` */ ifNotExists(): ColumnDefinitionBuilder { return new ColumnDefinitionBuilder( @@ -392,10 +640,19 @@ export class ColumnDefinitionBuilder implements OperationNodeSource { * ### Examples * * ```ts - * db.schema.createTable('person') - * .addColumn('id', 'integer', col => col.primaryKey()) - * .addColumn('age', 'integer', col => col.unsigned().notNull().modifyEnd(sql`comment ${sql.lit('it is not polite to ask a woman her age')}`)) - * .execute() + * import { sql } from 'kysely' + * + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.primaryKey()) + * .addColumn( + * 'age', + * 'integer', + * col => col.unsigned() + * .notNull() + * .modifyEnd(sql`comment ${sql.lit('it is not polite to ask a woman her age')}`) + * ) + * .execute() * ``` * * The generated SQL (MySQL): diff --git a/src/schema/create-table-builder.ts b/src/schema/create-table-builder.ts index 09de9fefd..d83068c35 100644 --- a/src/schema/create-table-builder.ts +++ b/src/schema/create-table-builder.ts @@ -116,9 +116,13 @@ export class CreateTableBuilder * ```ts * await db.schema * .createTable('person') + * .addColumn('id', 'integer', (col) => col.primaryKey()) * .addColumn('parent_id', 'integer') * .addForeignKeyConstraint( - * 'person_parent_id_fk', ['parent_id'], 'person', ['id'], + * 'person_parent_id_fk', + * ['parent_id'], + * 'person', + * ['id'], * (cb) => cb.onDelete('cascade') * ) * .execute() @@ -167,7 +171,12 @@ export class CreateTableBuilder * ### Examples * * ```ts - * addPrimaryKeyConstraint('primary_key', ['first_name', 'last_name']) + * await db.schema + * .createTable('person') + * .addColumn('first_name', 'varchar(64)') + * .addColumn('last_name', 'varchar(64)') + * .addPrimaryKeyConstraint('primary_key', ['first_name', 'last_name']) + * .execute() * ``` */ addPrimaryKeyConstraint( @@ -192,12 +201,30 @@ export class CreateTableBuilder * ### Examples * * ```ts - * addUniqueConstraint('first_name_last_name_unique', ['first_name', 'last_name']) + * await db.schema + * .createTable('person') + * .addColumn('first_name', 'varchar(64)') + * .addColumn('last_name', 'varchar(64)') + * .addUniqueConstraint( + * 'first_name_last_name_unique', + * ['first_name', 'last_name'] + * ) + * .execute() * ``` * * In dialects such as PostgreSQL you can specify `nulls not distinct` as follows: + * * ```ts - * addUniqueConstraint('first_name_last_name_unique', ['first_name', 'last_name'], (builder) => builder.nullsNotDistinct()) + * await db.schema + * .createTable('person') + * .addColumn('first_name', 'varchar(64)') + * .addColumn('last_name', 'varchar(64)') + * .addUniqueConstraint( + * 'first_name_last_name_unique', + * ['first_name', 'last_name'], + * (cb) => cb.nullsNotDistinct() + * ) + * .execute() * ``` */ addUniqueConstraint( @@ -231,7 +258,11 @@ export class CreateTableBuilder * ```ts * import { sql } from 'kysely' * - * addCheckConstraint('check_legs', sql`number_of_legs < 5`) + * await db.schema + * .createTable('animal') + * .addColumn('number_of_legs', 'integer') + * .addCheckConstraint('check_legs', sql`number_of_legs < 5`) + * .execute() * ``` */ addCheckConstraint( @@ -259,24 +290,33 @@ export class CreateTableBuilder * ### Examples * * ```ts - * addForeignKeyConstraint( - * 'owner_id_foreign', - * ['owner_id'], - * 'person', - * ['id'], - * ) + * await db.schema + * .createTable('pet') + * .addColumn('owner_id', 'integer') + * .addForeignKeyConstraint( + * 'owner_id_foreign', + * ['owner_id'], + * 'person', + * ['id'], + * ) + * .execute() * ``` * * Add constraint for multiple columns: * * ```ts - * addForeignKeyConstraint( - * 'owner_id_foreign', - * ['owner_id1', 'owner_id2'], - * 'person', - * ['id1', 'id2'], - * (cb) => cb.onDelete('cascade') - * ) + * await db.schema + * .createTable('pet') + * .addColumn('owner_id1', 'integer') + * .addColumn('owner_id2', 'integer') + * .addForeignKeyConstraint( + * 'owner_id_foreign', + * ['owner_id1', 'owner_id2'], + * 'person', + * ['id1', 'id2'], + * (cb) => cb.onDelete('cascade') + * ) + * .execute() * ``` */ addForeignKeyConstraint( @@ -314,7 +354,10 @@ export class CreateTableBuilder * ### Examples * * ```ts - * await db.schema.createTable('person') + * import { sql } from 'kysely' + * + * await db.schema + * .createTable('person') * .modifyFront(sql`global temporary`) * .addColumn('id', 'integer', col => col.primaryKey()) * .addColumn('first_name', 'varchar(64)', col => col.notNull()) @@ -350,8 +393,11 @@ export class CreateTableBuilder * ### Examples * * ```ts - * await db.schema.createTable('person') - * .addColumn('id', 'integer', col => col => primaryKey()) + * import { sql } from 'kysely' + * + * await db.schema + * .createTable('person') + * .addColumn('id', 'integer', col => col.primaryKey()) * .addColumn('first_name', 'varchar(64)', col => col.notNull()) * .addColumn('last_name', 'varchar(64)', col => col.notNull()) * .modifyEnd(sql`collate utf8_unicode_ci`) @@ -384,7 +430,8 @@ export class CreateTableBuilder * ### Examples * * ```ts - * db.schema.createTable('copy') + * await db.schema + * .createTable('copy') * .temporary() * .as(db.selectFrom('person').select(['first_name', 'last_name'])) * .execute() @@ -412,17 +459,19 @@ export class CreateTableBuilder * ### Examples * * ```ts - * db.schema + * await db.schema * .createTable('test') * .$call((builder) => builder.addColumn('id', 'integer')) * .execute() * ``` * + * This is useful for creating reusable functions that can be called with a builder. + * * ```ts - * const addDefaultColumns = ( - * builder: CreateTableBuilder - * ) => { - * return builder + * import { type CreateTableBuilder, sql } from 'kysely' + * + * const addDefaultColumns = (ctb: CreateTableBuilder) => { + * return ctb * .addColumn('id', 'integer', (col) => col.notNull()) * .addColumn('created_at', 'date', (col) => * col.notNull().defaultTo(sql`now()`) @@ -432,7 +481,7 @@ export class CreateTableBuilder * ) * } * - * db.schema + * await db.schema * .createTable('test') * .$call(addDefaultColumns) * .execute() diff --git a/src/util/column-type.ts b/src/util/column-type.ts index c8614bd05..fc9daeb7d 100644 --- a/src/util/column-type.ts +++ b/src/util/column-type.ts @@ -16,7 +16,7 @@ import { DrainOuterGeneric } from './type-utils.js' * type is actually just a shortcut for the type in this example: * * ```ts - * ColumnType + * type GeneratedNumber = ColumnType * ``` * * The above example makes the column optional in inserts @@ -25,14 +25,14 @@ import { DrainOuterGeneric } from './type-utils.js' * can se the type as `never`: * * ```ts - * ColumnType + * type ReadonlyNumber = ColumnType * ``` * * Here's one more example where the type is different * for each different operation: * * ```ts - * ColumnType + * type UnupdateableDate = ColumnType * ``` */ export type ColumnType< @@ -50,12 +50,8 @@ export type ColumnType< * is the same for all selects, inserts and updates but the * column is optional for inserts and updates. * - * The implementation: - * - * ```ts - * // The update type is `S` instead of `S | undefined` because - * // updates are always optional --> no need to specify optionality. - * type Generated = ColumnType + * The update type is `S` instead of `S | undefined` because updates are always + * optional --> no need to specify optionality. * ``` */ export type Generated = ColumnType diff --git a/src/util/require-all-props.ts b/src/util/require-all-props.ts index 9d39ed2c3..e6de77547 100644 --- a/src/util/require-all-props.ts +++ b/src/util/require-all-props.ts @@ -25,14 +25,16 @@ type AllProps = T & { [P in keyof T]-?: unknown } * 1. Omit checked type - all checked properties will be expect as of type never * * ```ts - * const z: SomeType = requireAllProps({ propC: "no type will work" }); + * type SomeType = { propA: string; propB?: number; } + * // const z: SomeType = requireAllProps({ propC: "no type will work" }); // Property 'propA' is missing in type '{ propC: string; }' but required in type 'SomeType'. * ``` * * 2. Apply to spreaded object - there is no way how to check in compile time if spreaded object contains all properties * * ```ts + * type SomeType = { propA: string; propB?: number; } * const y: SomeType = { propA: "" }; // valid object according to SomeType declaration - * const x = requireAllProps( { ... y } ); + * // const x = requireAllProps({ ...y }); // Argument of type '{ propA: string; propB?: number; }' is not assignable to parameter of type 'AllProps'. * ``` * * @param obj object to check if all properties has been used diff --git a/src/util/type-utils.ts b/src/util/type-utils.ts index 08d702e2d..e9fd78595 100644 --- a/src/util/type-utils.ts +++ b/src/util/type-utils.ts @@ -173,7 +173,9 @@ export type NarrowPartial = DrainOuterGeneric< * Example: * * ```ts - * const person = await db.selectFrom('person') + * import type { NotNull } from 'kysely' + * + * await db.selectFrom('person') * .where('nullable_column', 'is not', null) * .selectAll() * .$narrowType<{ nullable_column: NotNull }>() @@ -196,7 +198,7 @@ export type SqlBool = boolean | 0 | 1 * A>>>>>>>>>>>>>>>>>>>>>>> * > * - * type Error = Test // Type instantiation is excessively deep and possibly infinite.ts (2589) + * // type Error = Test // Type instantiation is excessively deep and possibly infinite.ts (2589) * ``` * * To fix this, we can use `DrainOuterGeneric`: