-
Notifications
You must be signed in to change notification settings - Fork 280
/
column-type.ts
195 lines (182 loc) · 4.66 KB
/
column-type.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/**
* This type can be used to specify a different type for
* select, insert and update operations.
*
* Also see the {@link Generated} type.
*
* ### Examples
*
* The next example defines a number column that is optional
* in inserts and updates. All columns are always optional
* in updates so therefore we don't need to specify `undefined`
* for the update type. The type below is useful for all kinds of
* database generated columns like identifiers. The `Generated`
* type is actually just a shortcut for the type in this example:
*
* ```ts
* ColumnType<number, number | undefined, number>
* ```
*
* The above example makes the column optional in inserts
* and updates, but you can still choose to provide the
* column. If you want to prevent insertion/update you
* can se the type as `never`:
*
* ```ts
* ColumnType<number, never, never>
* ```
*
* Here's one more example where the type is different
* for each different operation:
*
* ```ts
* ColumnType<Date, string, never>
* ```
*/
export type ColumnType<
SelectType,
InsertType = SelectType,
UpdateType = SelectType
> = {
readonly __select__: SelectType
readonly __insert__: InsertType
readonly __update__: UpdateType
}
/**
* A shortcut for defining database-generated columns. The type
* 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<S> = ColumnType<S, S | undefined, S>
* ```
*/
export type Generated<S> = ColumnType<S, S | undefined, S>
/**
* A shortcut for defining columns that are only database-generated
* (like postgres GENERATED ALWAYS AS IDENTITY). No insert/update
* is allowed.
*/
export type GeneratedAlways<S> = ColumnType<S, never, never>
/**
* Evaluates to `K` if `T` can be `null` or `undefined`.
*/
type IfNullable<T, K> = undefined extends T ? K : null extends T ? K : never
/**
* Evaluates to `K` if `T` can't be `null` or `undefined`.
*/
type IfNotNullable<T, K> = undefined extends T
? never
: null extends T
? never
: T extends never
? never
: K
/**
* Evaluates to `K` if `T` isn't `never`.
*/
type IfNotNever<T, K> = T extends never ? never : K
export type SelectType<T> = T extends ColumnType<infer S, any, any> ? S : T
export type InsertType<T> = T extends ColumnType<any, infer I, any> ? I : T
export type UpdateType<T> = T extends ColumnType<any, any, infer U> ? U : T
/**
* Keys of `R` whose `InsertType` values can be `null` or `undefined`.
*/
export type NullableInsertKeys<R> = {
[K in keyof R]: IfNullable<InsertType<R[K]>, K>
}[keyof R]
/**
* Keys of `R` whose `InsertType` values can't be `null` or `undefined`.
*/
export type NonNullableInsertKeys<R> = {
[K in keyof R]: IfNotNullable<InsertType<R[K]>, K>
}[keyof R]
/**
* Keys of `R` whose `SelectType` values are not `never`
*/
type NonNeverSelectKeys<R> = {
[K in keyof R]: IfNotNever<SelectType<R[K]>, K>
}[keyof R]
/**
* Keys of `R` whose `UpdateType` values are not `never`
*/
export type UpdateKeys<R> = {
[K in keyof R]: IfNotNever<UpdateType<R[K]>, K>
}[keyof R]
/**
* Given a table interface, extracts the select type from all
* {@link ColumnType} types.
*
* ### Examples
*
* ```ts
* interface PersonTable {
* id: Generated<number>
* first_name: string
* modified_at: ColumnType<Date, string, never>
* }
*
* type Person = Selectable<PersonTable>
* // {
* // id: number,
* // first_name: string
* // modified_at: Date
* // }
* ```
*/
export type Selectable<R> = {
[K in NonNeverSelectKeys<R>]: SelectType<R[K]>
}
/**
* Given a table interface, extracts the insert type from all
* {@link ColumnType} types.
*
* ### Examples
*
* ```ts
* interface PersonTable {
* id: Generated<number>
* first_name: string
* modified_at: ColumnType<Date, string, never>
* }
*
* type InsertablePerson = Insertable<PersonTable>
* // {
* // id?: number,
* // first_name: string
* // modified_at: string
* // }
* ```
*/
export type Insertable<R> = {
[K in NonNullableInsertKeys<R>]: InsertType<R[K]>
} & {
[K in NullableInsertKeys<R>]?: InsertType<R[K]>
}
/**
* Given a table interface, extracts the update type from all
* {@link ColumnType} types.
*
* ### Examples
*
* ```ts
* interface PersonTable {
* id: Generated<number>
* first_name: string
* modified_at: ColumnType<Date, string, never>
* }
*
* type UpdateablePerson = Updateable<PersonTable>
* // {
* // id?: number,
* // first_name?: string
* // }
* ```
*/
export type Updateable<R> = {
[K in UpdateKeys<R>]?: UpdateType<R[K]>
}