Created
December 31, 2021 12:49
-
-
Save timvandam/a4e8ff701f3fda93ac5a22fb4fe023e6 to your computer and use it in GitHub Desktop.
Type-stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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