Skip to content

Instantly share code, notes, and snippets.

@timvandam
Created December 31, 2021 12:49
Show Gist options
  • Save timvandam/a4e8ff701f3fda93ac5a22fb4fe023e6 to your computer and use it in GitHub Desktop.
Save timvandam/a4e8ff701f3fda93ac5a22fb4fe023e6 to your computer and use it in GitHub Desktop.
Type-stuff
export type PrimaryKey<Type> = Metadata<Type, 'primaryKey', { primaryKey: true }>;
export type BackReference<ReferencedBy, RelationName extends string> = Metadata<
ReferencedBy,
'backReference',
{ backReference: true; relationName: RelationName }
>;
export type GetBackReferenceRelationNames<Entity> = string &
{
[K in keyof Entity]: Entity[K] extends BackReference<any, infer RelationName>
? RelationName
: never;
}[keyof Entity];
export type GetFieldNameForBackReferenceRelationName<
Entity,
RelationName extends GetBackReferenceRelationNames<Entity>,
> = {
[K in keyof Entity]: Entity[K] extends BackReference<unknown, RelationName> ? K : never;
}[keyof Entity];
export type GetBackReferenceTypeByRelationName<
Entity,
RelationName extends GetBackReferenceRelationNames<Entity>,
> = {
[K in keyof Entity]: Entity[K] extends BackReference<infer T, RelationName> ? T : never;
}[keyof Entity];
export type Reference<References, RelationName extends string> = Metadata<
References,
'reference',
{ reference: true; relationName: RelationName }
>;
export type GetReferenceRelationNames<Entity> = string &
{
[K in keyof Entity]: Entity[K] extends Reference<unknown, infer RelationName>
? RelationName
: never;
}[keyof Entity];
export type GetFieldNameForReferenceRelationName<
Entity,
RelationName extends GetReferenceRelationNames<Entity>,
> = {
[K in keyof Entity]: Entity[K] extends Reference<unknown, RelationName> ? K : never;
}[keyof Entity];
export type GetReferenceTypeByRelationName<
Entity,
RelationName extends GetReferenceRelationNames<Entity>,
> = {
[K in keyof Entity]: Entity[K] extends Reference<infer T, RelationName> ? T : never;
}[keyof Entity];
export type GetRelationNames<Entity> =
| GetReferenceRelationNames<Entity>
| GetBackReferenceRelationNames<Entity>;
export type GetRelationNamesWithRelationTo<Entity, RelationTo> =
| (GetReferenceTypeByRelationName<Entity, GetReferenceRelationNames<Entity>> extends RelationTo
? GetReferenceRelationNames<Entity>
: never)
| (GetBackReferenceTypeByRelationName<
Entity,
GetBackReferenceRelationNames<Entity>
> extends RelationTo
? GetBackReferenceRelationNames<Entity>
: never);
export type SqlMetadata = {
leftJoin: JoinMetadata;
innerJoin: JoinMetadata;
reference: { reference: true; relationName: string };
backReference: { backReference: true; relationName: string };
primaryKey: { primaryKey: true };
};
export type MetadataKey = 'meta__';
export type Metadata<
Type,
Name extends keyof SqlMetadata,
Data extends SqlMetadata[Name],
> = Type & {
[K1 in MetadataKey]: { [K2 in Name]: Data };
};
export type NoMetadata<Type> = { [K in keyof Omit<Type, MetadataKey>]: NoMetadata<Type[K]> };
// Add metadata to a specific field
export type FieldMetadata<
Type,
FieldName extends keyof Type,
Name extends keyof SqlMetadata,
Data extends SqlMetadata[Name],
> = Omit<Type, FieldName> & { [K in FieldName]: Metadata<Type[FieldName], Name, Data> };
export type Resolve<Entity> = { [K in keyof Entity]: ResolveField<Entity[K]> };
type ResolveField<Field> = { [K in keyof Omit<Field, MetadataKey>]: ResolveField<Field[K]> };
export type JoinMetadata = { join: true; joinType: JoinTypes; relationName: string };
type JoinTypes = 'leftJoin' | 'innerJoin';
type JoinReference<
JoinType extends JoinTypes,
Entity,
RelationName extends GetReferenceRelationNames<Entity>,
> = FieldMetadata<
Entity,
GetFieldNameForReferenceRelationName<Entity, RelationName>,
JoinType,
{
join: true;
joinType: JoinType;
relationName: RelationName;
}
>;
type JoinBackReference<
JoinType extends JoinTypes,
Entity,
RelationName extends GetBackReferenceRelationNames<Entity>,
> = FieldMetadata<
Entity,
GetFieldNameForBackReferenceRelationName<Entity, RelationName>,
JoinType,
{
join: true;
joinType: JoinType;
relationName: RelationName;
}
>;
export type LeftJoin<
Entity,
RelationName extends GetRelationNames<Entity>,
> = RelationName extends GetReferenceRelationNames<Entity>
? JoinReference<'leftJoin', Entity, RelationName>
: RelationName extends GetBackReferenceRelationNames<Entity>
? JoinBackReference<'leftJoin', Entity, RelationName>
: never;
export type InnerJoin<
Entity,
RelationName extends GetRelationNames<Entity>,
> = RelationName extends GetReferenceRelationNames<Entity>
? JoinReference<'innerJoin', Entity, RelationName>
: RelationName extends GetBackReferenceRelationNames<Entity>
? JoinBackReference<'innerJoin', Entity, RelationName>
: never;
export interface SqlQuery<Entity> {
leftJoin<RelationName extends GetRelationNames<Entity>>(
name: RelationName,
): SqlQuery<LeftJoin<Entity, RelationName>>;
innerJoin<RelationName extends GetRelationNames<Entity>>(
name: RelationName,
): SqlQuery<InnerJoin<Entity, RelationName>>;
resolve(): Resolve<Entity>;
}
// THE IMPORTANT PART
type User = {
id: PrimaryKey<number>;
name: string;
credentials: BackReference<Credentials, 'user-credentials'>;
};
type Credentials = {
id: PrimaryKey<number>;
password: string;
user: Reference<User, 'user-credentials'>;
};
declare const userQuery: SqlQuery<User>;
declare const credentialsQuery: SqlQuery<Credentials>;
type Simplify<Obj> = { [K in keyof Obj]: Obj[K] };
const x = userQuery.leftJoin('user-credentials').resolve();
type X = typeof x;
type Y = Simplify<X>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment