@warp-drive/core 5.8.0-alpha.11 → 5.8.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/declarations/types/record.d.ts +132 -0
- package/dist/types/-private.js +1 -1
- package/dist/types/record.js +127 -0
- package/package.json +3 -3
|
@@ -134,4 +134,136 @@ export type StringSatisfiesIncludes<
|
|
|
134
134
|
SET extends string
|
|
135
135
|
> = _StringSatisfiesIncludes<T, SET, T>;
|
|
136
136
|
export declare function createIncludeValidator<T extends TypedRecordInstance>(): <U extends string>(includes: StringSatisfiesIncludes<U, Includes<T>>) => U;
|
|
137
|
+
/**
|
|
138
|
+
* A utility that takes two types, K and T, and produces a new type that is a "mask" of T based on K.
|
|
139
|
+
*
|
|
140
|
+
* That's a mouthful, so let's break it down:
|
|
141
|
+
*
|
|
142
|
+
* Let's say you have a User type and an Address type.
|
|
143
|
+
*
|
|
144
|
+
* ```ts
|
|
145
|
+
* interface Address {
|
|
146
|
+
* street: string;
|
|
147
|
+
* city: string;
|
|
148
|
+
* state: string;
|
|
149
|
+
* zip: string;
|
|
150
|
+
* }
|
|
151
|
+
*
|
|
152
|
+
* interface User {
|
|
153
|
+
* name: string;
|
|
154
|
+
* title: string;
|
|
155
|
+
* address: Address;
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*
|
|
159
|
+
* Now, imagine you want to load a preview of the user with some information about their address,
|
|
160
|
+
* but you don't want to load the entire user or address. You probably want to still ensure
|
|
161
|
+
* the type of the data you do load matches the underlying Address and User types, but doesn't
|
|
162
|
+
* include everything.
|
|
163
|
+
*
|
|
164
|
+
* If you did this manually, you might do something like this:
|
|
165
|
+
*
|
|
166
|
+
* ```ts
|
|
167
|
+
* interface UserPreview {
|
|
168
|
+
* name: string;
|
|
169
|
+
* address: AddressPreview;
|
|
170
|
+
* }
|
|
171
|
+
*
|
|
172
|
+
* interface AddressPreview {
|
|
173
|
+
* city: string;
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* From a TypeScript performance perspective, this is the best way to approach these preview
|
|
178
|
+
* types, but this is also tedious and error-prone, especially if the User or Address types change.
|
|
179
|
+
*
|
|
180
|
+
* For Address, we could create a validated type using `Pick`:
|
|
181
|
+
*
|
|
182
|
+
* ```ts
|
|
183
|
+
* type AddressPreview = Pick<Address, 'city'>;
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* This ensures that if the Address type changes, our AddressPreview will still be valid.
|
|
187
|
+
* However, for UserPreview, we can't just use `Pick` because the `address` property is of type `Address`,
|
|
188
|
+
* not `AddressPreview`. This is where the `Mask` type comes in.
|
|
189
|
+
*
|
|
190
|
+
* With `Mask`, we define the `UserPreview` in two parts
|
|
191
|
+
* - first, we define the subset of fields we want to include from `User`, using `Pick` or an interface.
|
|
192
|
+
* - then, we use `Mask` to replace the related types of fields like Address with their more limited subset.
|
|
193
|
+
*
|
|
194
|
+
* Here's how we can do it:
|
|
195
|
+
*
|
|
196
|
+
* ```ts
|
|
197
|
+
* // First, we define the base of UserPreview with Pick
|
|
198
|
+
* type UserPreviewBase = Pick<User, 'name' | 'address'>;
|
|
199
|
+
* // Then, we use Mask to replace Address with AddressPreview
|
|
200
|
+
* type UserPreview = Mask<{ address: AddressPreview }, UserPreviewBase>;
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* Now, `UserPreview` will have the `name` field from `User` and the `address` field will be of type `AddressPreview`.
|
|
204
|
+
* This way, if the `User` or `Address` types change, TypeScript will ensure that our `UserPreview` and `AddressPreview`
|
|
205
|
+
* types remain valid and consistent with the underlying types.
|
|
206
|
+
*
|
|
207
|
+
* But what if your app has data with massive interfaces such that the TypeScript performance of this
|
|
208
|
+
* approach becomes a problem? In that case, see {@link Validate}
|
|
209
|
+
*/
|
|
210
|
+
export type Mask<
|
|
211
|
+
K extends object,
|
|
212
|
+
T extends K
|
|
213
|
+
> = { [P in keyof T] : P extends keyof K ? (T[P] extends K[P] ? K[P] : never) : T[P] };
|
|
214
|
+
/**
|
|
215
|
+
* A utility that takes two types, K and T, and ensures that K is a valid subset of T.
|
|
216
|
+
*
|
|
217
|
+
* That's a mouthful, so let's break it down:
|
|
218
|
+
*
|
|
219
|
+
* Let's say you have a User type and an Address type.
|
|
220
|
+
*
|
|
221
|
+
* ```ts
|
|
222
|
+
* interface Address {
|
|
223
|
+
* street: string;
|
|
224
|
+
* city: string;
|
|
225
|
+
* state: string;
|
|
226
|
+
* zip: string;
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* interface User {
|
|
230
|
+
* name: string;
|
|
231
|
+
* title: string;
|
|
232
|
+
* address: Address;
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* Now, imagine you want to load a preview of the user with some information about their address,
|
|
237
|
+
* but you don't want to load the entire user or address. You probably want to still ensure
|
|
238
|
+
* the type of the data you do load matches the underlying Address and User types, but doesn't
|
|
239
|
+
* include everything.
|
|
240
|
+
*
|
|
241
|
+
* You might do something like this:
|
|
242
|
+
*
|
|
243
|
+
* ```ts
|
|
244
|
+
* interface UserPreview {
|
|
245
|
+
* name: string;
|
|
246
|
+
* address: AddressPreview;
|
|
247
|
+
* }
|
|
248
|
+
*
|
|
249
|
+
* interface AddressPreview {
|
|
250
|
+
* city: string;
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*
|
|
254
|
+
* From a TypeScript performance perspective, this is the best way to approach these preview
|
|
255
|
+
* types, but this is also error-prone, especially if the User or Address types change.
|
|
256
|
+
*
|
|
257
|
+
* Validate can help ensure that your preview types remain valid.
|
|
258
|
+
*
|
|
259
|
+
* ```ts
|
|
260
|
+
* type IsValidUserPreview = Validate<UserPreview, User>; // This will be valid
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* For help creating subsets of types, see {@link Mask}
|
|
264
|
+
*/
|
|
265
|
+
export type Validate<
|
|
266
|
+
K extends object,
|
|
267
|
+
T extends K
|
|
268
|
+
> = T extends K ? K : never;
|
|
137
269
|
export {};
|
package/dist/types/-private.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { macroCondition, getGlobalConfig } from '@embroider/macros';
|
|
2
2
|
const name = "@warp-drive/core";
|
|
3
|
-
const version = "5.8.0-alpha.
|
|
3
|
+
const version = "5.8.0-alpha.12";
|
|
4
4
|
|
|
5
5
|
// in testing mode, we utilize globals to ensure only one copy exists of
|
|
6
6
|
// these maps, due to bugs in ember-auto-import
|
package/dist/types/record.js
CHANGED
|
@@ -61,4 +61,131 @@ function createIncludeValidator() {
|
|
|
61
61
|
return includes;
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A utility that takes two types, K and T, and produces a new type that is a "mask" of T based on K.
|
|
67
|
+
*
|
|
68
|
+
* That's a mouthful, so let's break it down:
|
|
69
|
+
*
|
|
70
|
+
* Let's say you have a User type and an Address type.
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* interface Address {
|
|
74
|
+
* street: string;
|
|
75
|
+
* city: string;
|
|
76
|
+
* state: string;
|
|
77
|
+
* zip: string;
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* interface User {
|
|
81
|
+
* name: string;
|
|
82
|
+
* title: string;
|
|
83
|
+
* address: Address;
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* Now, imagine you want to load a preview of the user with some information about their address,
|
|
88
|
+
* but you don't want to load the entire user or address. You probably want to still ensure
|
|
89
|
+
* the type of the data you do load matches the underlying Address and User types, but doesn't
|
|
90
|
+
* include everything.
|
|
91
|
+
*
|
|
92
|
+
* If you did this manually, you might do something like this:
|
|
93
|
+
*
|
|
94
|
+
* ```ts
|
|
95
|
+
* interface UserPreview {
|
|
96
|
+
* name: string;
|
|
97
|
+
* address: AddressPreview;
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* interface AddressPreview {
|
|
101
|
+
* city: string;
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* From a TypeScript performance perspective, this is the best way to approach these preview
|
|
106
|
+
* types, but this is also tedious and error-prone, especially if the User or Address types change.
|
|
107
|
+
*
|
|
108
|
+
* For Address, we could create a validated type using `Pick`:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* type AddressPreview = Pick<Address, 'city'>;
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* This ensures that if the Address type changes, our AddressPreview will still be valid.
|
|
115
|
+
* However, for UserPreview, we can't just use `Pick` because the `address` property is of type `Address`,
|
|
116
|
+
* not `AddressPreview`. This is where the `Mask` type comes in.
|
|
117
|
+
*
|
|
118
|
+
* With `Mask`, we define the `UserPreview` in two parts
|
|
119
|
+
* - first, we define the subset of fields we want to include from `User`, using `Pick` or an interface.
|
|
120
|
+
* - then, we use `Mask` to replace the related types of fields like Address with their more limited subset.
|
|
121
|
+
*
|
|
122
|
+
* Here's how we can do it:
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* // First, we define the base of UserPreview with Pick
|
|
126
|
+
* type UserPreviewBase = Pick<User, 'name' | 'address'>;
|
|
127
|
+
* // Then, we use Mask to replace Address with AddressPreview
|
|
128
|
+
* type UserPreview = Mask<{ address: AddressPreview }, UserPreviewBase>;
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* Now, `UserPreview` will have the `name` field from `User` and the `address` field will be of type `AddressPreview`.
|
|
132
|
+
* This way, if the `User` or `Address` types change, TypeScript will ensure that our `UserPreview` and `AddressPreview`
|
|
133
|
+
* types remain valid and consistent with the underlying types.
|
|
134
|
+
*
|
|
135
|
+
* But what if your app has data with massive interfaces such that the TypeScript performance of this
|
|
136
|
+
* approach becomes a problem? In that case, see {@link Validate}
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* A utility that takes two types, K and T, and ensures that K is a valid subset of T.
|
|
141
|
+
*
|
|
142
|
+
* That's a mouthful, so let's break it down:
|
|
143
|
+
*
|
|
144
|
+
* Let's say you have a User type and an Address type.
|
|
145
|
+
*
|
|
146
|
+
* ```ts
|
|
147
|
+
* interface Address {
|
|
148
|
+
* street: string;
|
|
149
|
+
* city: string;
|
|
150
|
+
* state: string;
|
|
151
|
+
* zip: string;
|
|
152
|
+
* }
|
|
153
|
+
*
|
|
154
|
+
* interface User {
|
|
155
|
+
* name: string;
|
|
156
|
+
* title: string;
|
|
157
|
+
* address: Address;
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* Now, imagine you want to load a preview of the user with some information about their address,
|
|
162
|
+
* but you don't want to load the entire user or address. You probably want to still ensure
|
|
163
|
+
* the type of the data you do load matches the underlying Address and User types, but doesn't
|
|
164
|
+
* include everything.
|
|
165
|
+
*
|
|
166
|
+
* You might do something like this:
|
|
167
|
+
*
|
|
168
|
+
* ```ts
|
|
169
|
+
* interface UserPreview {
|
|
170
|
+
* name: string;
|
|
171
|
+
* address: AddressPreview;
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* interface AddressPreview {
|
|
175
|
+
* city: string;
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*
|
|
179
|
+
* From a TypeScript performance perspective, this is the best way to approach these preview
|
|
180
|
+
* types, but this is also error-prone, especially if the User or Address types change.
|
|
181
|
+
*
|
|
182
|
+
* Validate can help ensure that your preview types remain valid.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* type IsValidUserPreview = Validate<UserPreview, User>; // This will be valid
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* For help creating subsets of types, see {@link Mask}
|
|
189
|
+
*/
|
|
190
|
+
|
|
64
191
|
export { createIncludeValidator };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warp-drive/core",
|
|
3
|
-
"version": "5.8.0-alpha.
|
|
3
|
+
"version": "5.8.0-alpha.12",
|
|
4
4
|
"description": "Core package for WarpDrive | All the Universal Basics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@embroider/macros": "^1.18.1",
|
|
40
|
-
"@warp-drive/build-config": "5.8.0-alpha.
|
|
40
|
+
"@warp-drive/build-config": "5.8.0-alpha.12"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@babel/core": "^7.28.3",
|
|
44
44
|
"@babel/plugin-transform-typescript": "^7.28.0",
|
|
45
45
|
"@babel/preset-typescript": "^7.27.1",
|
|
46
|
-
"@warp-drive/internal-config": "5.8.0-alpha.
|
|
46
|
+
"@warp-drive/internal-config": "5.8.0-alpha.12",
|
|
47
47
|
"decorator-transforms": "^2.3.0",
|
|
48
48
|
"ember-source": "~6.6.0",
|
|
49
49
|
"expect-type": "^1.2.2",
|