0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

feat: better Table object for type inference

This commit is contained in:
bholmesdev 2024-01-24 18:39:17 -05:00
parent 76f8c76ea8
commit bfd2706378
3 changed files with 54 additions and 53 deletions

View file

@ -35,15 +35,7 @@ import { nanoid } from 'nanoid';
import type { AstroIntegrationLogger } from 'astro';
export type SqliteDB = SqliteRemoteDatabase;
export type {
AstroTable,
AstroText,
AstroDate,
AstroBoolean,
AstroNumber,
AstroJson,
AstroId,
} from './types.js';
export type { Table } from './types.js';
const sqlite = new SQLiteAsyncDialect();

View file

@ -25,40 +25,21 @@ ${Object.entries(collections)
}
function generateTableType(name: string, collection: DBCollection): string {
let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).AstroTable<{
name: ${JSON.stringify(name)};
columns: {
id: import(${INTERNAL_MOD_IMPORT}).AstroId<{
tableName: ${JSON.stringify(name)};
}>;`;
for (const [fieldName, field] of Object.entries(collection.fields)) {
const drizzleInterface = schemaTypeToDrizzleInterface(field.type);
tableType += `
${fieldName}: import(${INTERNAL_MOD_IMPORT}).${drizzleInterface}<{
tableName: ${JSON.stringify(name)};
name: ${JSON.stringify(fieldName)};
notNull: ${field.optional ? 'false' : 'true'};
hasDefault: ${typeof field.default !== 'undefined' ? 'true' : 'false'};
}>;`;
}
tableType += `
};
}>;`;
let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).Table<
${JSON.stringify(name)},
${JSON.stringify(
Object.fromEntries(
Object.entries(collection.fields).map(([fieldName, field]) => [
fieldName,
{
// Only select fields Drizzle needs for inference
type: field.type,
optional: field.optional,
default: field.default,
},
])
)
)}
>;`;
return tableType;
}
function schemaTypeToDrizzleInterface(type: FieldType) {
switch (type) {
case 'text':
return 'AstroText';
case 'number':
return 'AstroNumber';
case 'boolean':
return 'AstroBoolean';
case 'date':
return 'AstroDate';
case 'json':
return 'AstroJson';
}
}

View file

@ -1,5 +1,5 @@
import type { ColumnDataType, ColumnBaseConfig } from 'drizzle-orm';
import type { SQLiteColumn, SQLiteTableWithColumns, TableConfig } from 'drizzle-orm/sqlite-core';
import type { SQLiteColumn, SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core';
import { z } from 'zod';
const baseFieldSchema = z.object({
@ -98,14 +98,7 @@ export type DBCollections = Record<string, DBCollection>;
export type ReadableDBCollection = z.infer<typeof readableCollectionSchema>;
export type WritableDBCollection = z.infer<typeof writableCollectionSchema>;
export type AstroTable<T extends Pick<TableConfig, 'name' | 'columns'>> = SQLiteTableWithColumns<
T & {
schema: undefined;
dialect: 'sqlite';
}
>;
type GeneratedConfig<T extends ColumnDataType> = Pick<
type GeneratedConfig<T extends ColumnDataType = ColumnDataType> = Pick<
ColumnBaseConfig<T, string>,
'name' | 'tableName' | 'notNull' | 'hasDefault'
>;
@ -180,3 +173,38 @@ export type AstroId<T extends Pick<GeneratedConfig<'string'>, 'tableName'>> = SQ
>;
export type MaybePromise<T> = T | Promise<T>;
export type Column<T extends DBField['type'], S extends GeneratedConfig> = T extends 'boolean'
? AstroBoolean<S>
: T extends 'number'
? AstroNumber<S>
: T extends 'text'
? AstroText<S>
: T extends 'date'
? AstroDate<S>
: T extends 'json'
? AstroJson<S>
: never;
export type Table<
TTableName extends string,
TFields extends Record<string, Pick<DBField, 'type' | 'default' | 'optional'>>,
> = SQLiteTableWithColumns<{
name: TTableName;
schema: undefined;
dialect: 'sqlite';
columns: {
id: AstroId<{ tableName: TTableName }>;
} & {
[K in keyof TFields]: Column<
TFields[K]['type'],
{
tableName: TTableName;
// TODO: narrow K to string
name: string;
hasDefault: TFields[K]['default'] extends undefined ? false : true;
notNull: TFields[K]['optional'] extends true ? false : true;
}
>;
};
}>;