@octanejs/remix-router 0.1.0
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/LICENSE +21 -0
- package/README.md +90 -0
- package/package.json +53 -0
- package/src/dom.ts +16 -0
- package/src/index.ts +267 -0
- package/src/internal.ts +37 -0
- package/src/lib/Await.tsrx +145 -0
- package/src/lib/Await.tsrx.d.ts +6 -0
- package/src/lib/DefaultErrorComponent.tsrx +47 -0
- package/src/lib/DefaultErrorComponent.tsrx.d.ts +2 -0
- package/src/lib/RenderErrorBoundary.tsrx +117 -0
- package/src/lib/RenderErrorBoundary.tsrx.d.ts +14 -0
- package/src/lib/actions.ts +90 -0
- package/src/lib/components/MemoryRouter.tsrx +42 -0
- package/src/lib/components/MemoryRouter.tsrx.d.ts +10 -0
- package/src/lib/components/Navigate.ts +60 -0
- package/src/lib/components/Router.tsrx +88 -0
- package/src/lib/components/Router.tsrx.d.ts +13 -0
- package/src/lib/components/RouterProvider.tsrx +320 -0
- package/src/lib/components/RouterProvider.tsrx.d.ts +21 -0
- package/src/lib/components/Routes.tsrx +53 -0
- package/src/lib/components/Routes.tsrx.d.ts +7 -0
- package/src/lib/components/routes-collector.ts +317 -0
- package/src/lib/components/utils.ts +171 -0
- package/src/lib/components/with-props.ts +72 -0
- package/src/lib/context.ts +129 -0
- package/src/lib/dom/Form.tsrx +76 -0
- package/src/lib/dom/Form.tsrx.d.ts +22 -0
- package/src/lib/dom/Link.tsrx +91 -0
- package/src/lib/dom/Link.tsrx.d.ts +22 -0
- package/src/lib/dom/NavLink.tsrx +118 -0
- package/src/lib/dom/NavLink.tsrx.d.ts +33 -0
- package/src/lib/dom/dom.ts +344 -0
- package/src/lib/dom/hooks.ts +93 -0
- package/src/lib/dom/lib.ts +822 -0
- package/src/lib/dom/routers.tsrx +104 -0
- package/src/lib/dom/routers.tsrx.d.ts +23 -0
- package/src/lib/dom/server.tsrx +350 -0
- package/src/lib/dom/server.tsrx.d.ts +25 -0
- package/src/lib/errors.ts +84 -0
- package/src/lib/framework-stubs.ts +103 -0
- package/src/lib/hooks.ts +1797 -0
- package/src/lib/href.ts +71 -0
- package/src/lib/react-types.ts +10 -0
- package/src/lib/router/history.ts +763 -0
- package/src/lib/router/instrumentation.ts +496 -0
- package/src/lib/router/links.ts +192 -0
- package/src/lib/router/router.ts +7165 -0
- package/src/lib/router/server-runtime-types.ts +12 -0
- package/src/lib/router/url.ts +8 -0
- package/src/lib/router/utils.ts +2179 -0
- package/src/lib/server-runtime/cookies.ts +240 -0
- package/src/lib/server-runtime/crypto.ts +55 -0
- package/src/lib/server-runtime/mode.ts +16 -0
- package/src/lib/server-runtime/sessions/cookieStorage.ts +54 -0
- package/src/lib/server-runtime/sessions/memoryStorage.ts +59 -0
- package/src/lib/server-runtime/sessions.ts +291 -0
- package/src/lib/server-runtime/warnings.ts +10 -0
- package/src/lib/types/future.ts +16 -0
- package/src/lib/types/params.ts +8 -0
- package/src/lib/types/register.ts +39 -0
- package/src/lib/types/route-module.ts +17 -0
- package/src/lib/types/utils.ts +37 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/server-runtime/sessions.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
import type { ParseOptions, SerializeOptions } from 'cookie';
|
|
4
|
+
|
|
5
|
+
import type { Cookie, CookieOptions } from './cookies';
|
|
6
|
+
import { createCookie, isCookie } from './cookies';
|
|
7
|
+
import { warnOnce } from './warnings';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An object of name/value pairs to be used in the session.
|
|
11
|
+
*/
|
|
12
|
+
export interface SessionData {
|
|
13
|
+
[name: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Session persists data across HTTP requests.
|
|
18
|
+
*
|
|
19
|
+
* @see https://reactrouter.com/explanation/sessions-and-cookies#sessions
|
|
20
|
+
*/
|
|
21
|
+
export interface Session<Data = SessionData, FlashData = Data> {
|
|
22
|
+
/**
|
|
23
|
+
* A unique identifier for this session.
|
|
24
|
+
*
|
|
25
|
+
* Note: This will be the empty string for newly created sessions and
|
|
26
|
+
* sessions that are not backed by a database (i.e. cookie-based sessions).
|
|
27
|
+
*/
|
|
28
|
+
readonly id: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The raw data contained in this session.
|
|
32
|
+
*
|
|
33
|
+
* This is useful mostly for SessionStorage internally to access the raw
|
|
34
|
+
* session data to persist.
|
|
35
|
+
*/
|
|
36
|
+
readonly data: FlashSessionData<Data, FlashData>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns `true` if the session has a value for the given `name`, `false`
|
|
40
|
+
* otherwise.
|
|
41
|
+
*/
|
|
42
|
+
has(name: (keyof Data | keyof FlashData) & string): boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the value for the given `name` in this session.
|
|
46
|
+
*/
|
|
47
|
+
get<Key extends (keyof Data | keyof FlashData) & string>(
|
|
48
|
+
name: Key,
|
|
49
|
+
):
|
|
50
|
+
| (Key extends keyof Data ? Data[Key] : undefined)
|
|
51
|
+
| (Key extends keyof FlashData ? FlashData[Key] : undefined)
|
|
52
|
+
| undefined;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sets a value in the session for the given `name`.
|
|
56
|
+
*/
|
|
57
|
+
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sets a value in the session that is only valid until the next `get()`.
|
|
61
|
+
* This can be useful for temporary values, like error messages.
|
|
62
|
+
*/
|
|
63
|
+
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Removes a value from the session.
|
|
67
|
+
*/
|
|
68
|
+
unset(name: keyof Data & string): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type FlashSessionData<Data, FlashData> = Partial<
|
|
72
|
+
Data & {
|
|
73
|
+
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
|
|
74
|
+
}
|
|
75
|
+
>;
|
|
76
|
+
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
|
|
77
|
+
function flash<Key extends string>(name: Key): FlashDataKey<Key> {
|
|
78
|
+
return `__flash_${name}__`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type CreateSessionFunction = <Data = SessionData, FlashData = Data>(
|
|
82
|
+
initialData?: Data,
|
|
83
|
+
id?: string,
|
|
84
|
+
) => Session<Data, FlashData>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates a new Session object.
|
|
88
|
+
*
|
|
89
|
+
* Note: This function is typically not invoked directly by application code.
|
|
90
|
+
* Instead, use a `SessionStorage` object's `getSession` method.
|
|
91
|
+
*/
|
|
92
|
+
export const createSession: CreateSessionFunction = <Data = SessionData, FlashData = Data>(
|
|
93
|
+
initialData: Partial<Data> = {},
|
|
94
|
+
id = '',
|
|
95
|
+
): Session<Data, FlashData> => {
|
|
96
|
+
let map = new Map(Object.entries(initialData)) as Map<
|
|
97
|
+
keyof Data | FlashDataKey<keyof FlashData & string>,
|
|
98
|
+
any
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
get id() {
|
|
103
|
+
return id;
|
|
104
|
+
},
|
|
105
|
+
get data() {
|
|
106
|
+
return Object.fromEntries(map) as FlashSessionData<Data, FlashData>;
|
|
107
|
+
},
|
|
108
|
+
has(name) {
|
|
109
|
+
return map.has(name as keyof Data) || map.has(flash(name as keyof FlashData & string));
|
|
110
|
+
},
|
|
111
|
+
get(name) {
|
|
112
|
+
if (map.has(name as keyof Data)) return map.get(name as keyof Data);
|
|
113
|
+
|
|
114
|
+
let flashName = flash(name as keyof FlashData & string);
|
|
115
|
+
if (map.has(flashName)) {
|
|
116
|
+
let value = map.get(flashName);
|
|
117
|
+
map.delete(flashName);
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return undefined;
|
|
122
|
+
},
|
|
123
|
+
set(name, value) {
|
|
124
|
+
map.set(name, value);
|
|
125
|
+
},
|
|
126
|
+
flash(name, value) {
|
|
127
|
+
map.set(flash(name), value);
|
|
128
|
+
},
|
|
129
|
+
unset(name) {
|
|
130
|
+
map.delete(name);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type IsSessionFunction = (object: any) => object is Session;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns true if an object is a React Router session.
|
|
139
|
+
*
|
|
140
|
+
* @see https://reactrouter.com/api/utils/isSession
|
|
141
|
+
*/
|
|
142
|
+
export const isSession: IsSessionFunction = (object): object is Session => {
|
|
143
|
+
return (
|
|
144
|
+
object != null &&
|
|
145
|
+
typeof object.id === 'string' &&
|
|
146
|
+
typeof object.data !== 'undefined' &&
|
|
147
|
+
typeof object.has === 'function' &&
|
|
148
|
+
typeof object.get === 'function' &&
|
|
149
|
+
typeof object.set === 'function' &&
|
|
150
|
+
typeof object.flash === 'function' &&
|
|
151
|
+
typeof object.unset === 'function'
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* SessionStorage stores session data between HTTP requests and knows how to
|
|
157
|
+
* parse and create cookies.
|
|
158
|
+
*
|
|
159
|
+
* A SessionStorage creates Session objects using a `Cookie` header as input.
|
|
160
|
+
* Then, later it generates the `Set-Cookie` header to be used in the response.
|
|
161
|
+
*/
|
|
162
|
+
export interface SessionStorage<Data = SessionData, FlashData = Data> {
|
|
163
|
+
/**
|
|
164
|
+
* Parses a Cookie header from a HTTP request and returns the associated
|
|
165
|
+
* Session. If there is no session associated with the cookie, this will
|
|
166
|
+
* return a new Session with no data.
|
|
167
|
+
*/
|
|
168
|
+
getSession: (
|
|
169
|
+
cookieHeader?: string | null,
|
|
170
|
+
options?: ParseOptions,
|
|
171
|
+
) => Promise<Session<Data, FlashData>>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Stores all data in the Session and returns the Set-Cookie header to be
|
|
175
|
+
* used in the HTTP response.
|
|
176
|
+
*/
|
|
177
|
+
commitSession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Deletes all data associated with the Session and returns the Set-Cookie
|
|
181
|
+
* header to be used in the HTTP response.
|
|
182
|
+
*/
|
|
183
|
+
destroySession: (
|
|
184
|
+
session: Session<Data, FlashData>,
|
|
185
|
+
options?: SerializeOptions,
|
|
186
|
+
) => Promise<string>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* SessionIdStorageStrategy is designed to allow anyone to easily build their
|
|
191
|
+
* own SessionStorage using `createSessionStorage(strategy)`.
|
|
192
|
+
*
|
|
193
|
+
* This strategy describes a common scenario where the session id is stored in
|
|
194
|
+
* a cookie but the actual session data is stored elsewhere, usually in a
|
|
195
|
+
* database or on disk. A set of create, read, update, and delete operations
|
|
196
|
+
* are provided for managing the session data.
|
|
197
|
+
*/
|
|
198
|
+
export interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
|
|
199
|
+
/**
|
|
200
|
+
* The Cookie used to store the session id, or options used to automatically
|
|
201
|
+
* create one.
|
|
202
|
+
*/
|
|
203
|
+
cookie?: Cookie | (CookieOptions & { name?: string });
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates a new record with the given data and returns the session id.
|
|
207
|
+
*/
|
|
208
|
+
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Returns data for a given session id, or `null` if there isn't any.
|
|
212
|
+
*/
|
|
213
|
+
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Updates data for the given session id.
|
|
217
|
+
*/
|
|
218
|
+
updateData: (
|
|
219
|
+
id: string,
|
|
220
|
+
data: FlashSessionData<Data, FlashData>,
|
|
221
|
+
expires?: Date,
|
|
222
|
+
) => Promise<void>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Deletes data for a given session id from the data store.
|
|
226
|
+
*/
|
|
227
|
+
deleteData: (id: string) => Promise<void>;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Creates a SessionStorage object using a SessionIdStorageStrategy.
|
|
232
|
+
*
|
|
233
|
+
* Note: This is a low-level API that should only be used if none of the
|
|
234
|
+
* existing session storage options meet your requirements.
|
|
235
|
+
*/
|
|
236
|
+
export function createSessionStorage<Data = SessionData, FlashData = Data>({
|
|
237
|
+
cookie: cookieArg,
|
|
238
|
+
createData,
|
|
239
|
+
readData,
|
|
240
|
+
updateData,
|
|
241
|
+
deleteData,
|
|
242
|
+
}: SessionIdStorageStrategy<Data, FlashData>): SessionStorage<Data, FlashData> {
|
|
243
|
+
let cookie = isCookie(cookieArg)
|
|
244
|
+
? cookieArg
|
|
245
|
+
: createCookie(cookieArg?.name || '__session', cookieArg);
|
|
246
|
+
|
|
247
|
+
warnOnceAboutSigningSessionCookie(cookie);
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
async getSession(cookieHeader, options) {
|
|
251
|
+
let id = cookieHeader && (await cookie.parse(cookieHeader, options));
|
|
252
|
+
let data = id && (await readData(id));
|
|
253
|
+
return createSession(data || {}, id || '');
|
|
254
|
+
},
|
|
255
|
+
async commitSession(session, options) {
|
|
256
|
+
let { id, data } = session;
|
|
257
|
+
let expires =
|
|
258
|
+
options?.maxAge != null
|
|
259
|
+
? new Date(Date.now() + options.maxAge * 1000)
|
|
260
|
+
: options?.expires != null
|
|
261
|
+
? options.expires
|
|
262
|
+
: cookie.expires;
|
|
263
|
+
|
|
264
|
+
if (id) {
|
|
265
|
+
await updateData(id, data, expires);
|
|
266
|
+
} else {
|
|
267
|
+
id = await createData(data, expires);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return cookie.serialize(id, options);
|
|
271
|
+
},
|
|
272
|
+
async destroySession(session, options) {
|
|
273
|
+
await deleteData(session.id);
|
|
274
|
+
return cookie.serialize('', {
|
|
275
|
+
...options,
|
|
276
|
+
maxAge: undefined,
|
|
277
|
+
expires: new Date(0),
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function warnOnceAboutSigningSessionCookie(cookie: Cookie) {
|
|
284
|
+
warnOnce(
|
|
285
|
+
cookie.isSigned,
|
|
286
|
+
`The "${cookie.name}" cookie is not signed, but session cookies should be ` +
|
|
287
|
+
`signed to prevent tampering on the client before they are sent back to the ` +
|
|
288
|
+
`server. See https://reactrouter.com/explanation/sessions-and-cookies#signing-cookies ` +
|
|
289
|
+
`for more information.`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/server-runtime/warnings.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
const alreadyWarned: { [message: string]: boolean } = {};
|
|
4
|
+
|
|
5
|
+
export function warnOnce(condition: boolean, message: string): void {
|
|
6
|
+
if (!condition && !alreadyWarned[message]) {
|
|
7
|
+
alreadyWarned[message] = true;
|
|
8
|
+
console.warn(message);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/types/future.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
/**
|
|
4
|
+
* An augmentable interface users can modify in their app-code to opt into
|
|
5
|
+
* future-flag-specific types
|
|
6
|
+
*/
|
|
7
|
+
export interface Future {
|
|
8
|
+
// We list the potential fields here in comments strictly for clarity.
|
|
9
|
+
// They will be generated by the react-router/dev/typegen/generate.ts module
|
|
10
|
+
//
|
|
11
|
+
// v8_middleware: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
export type MiddlewareEnabled =
|
|
16
|
+
Future extends { v8_middleware: infer T extends boolean; } ? T : false
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/types/params.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
import type { Pages, RouteFiles } from './register';
|
|
4
|
+
import type { Normalize } from './utils';
|
|
5
|
+
|
|
6
|
+
export type Params<RouteFile extends keyof RouteFiles> = Normalize<
|
|
7
|
+
Pages[RouteFiles[RouteFile]['page']]['params']
|
|
8
|
+
>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/types/register.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
import type { RouteModule } from './route-module';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Apps can use this interface to "register" app-wide types for React Router via interface declaration merging and module augmentation.
|
|
7
|
+
* React Router should handle this for you via type generation.
|
|
8
|
+
*
|
|
9
|
+
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
|
|
10
|
+
*/
|
|
11
|
+
export interface Register {
|
|
12
|
+
// pages
|
|
13
|
+
// routeFiles
|
|
14
|
+
// routeModules
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// pages
|
|
18
|
+
type AnyParams = Record<string, string | undefined>;
|
|
19
|
+
type AnyPages = Record<string, { params: AnyParams }>;
|
|
20
|
+
export type Pages = Register extends {
|
|
21
|
+
pages: infer Registered extends AnyPages;
|
|
22
|
+
}
|
|
23
|
+
? Registered
|
|
24
|
+
: AnyPages;
|
|
25
|
+
|
|
26
|
+
// route files
|
|
27
|
+
type AnyRouteFiles = Record<string, { id: string; page: string }>;
|
|
28
|
+
export type RouteFiles = Register extends {
|
|
29
|
+
routeFiles: infer Registered extends AnyRouteFiles;
|
|
30
|
+
}
|
|
31
|
+
? Registered
|
|
32
|
+
: AnyRouteFiles;
|
|
33
|
+
|
|
34
|
+
type AnyRouteModules = Record<string, RouteModule>;
|
|
35
|
+
export type RouteModules = Register extends {
|
|
36
|
+
routeModules: infer Registered extends AnyRouteModules;
|
|
37
|
+
}
|
|
38
|
+
? Registered
|
|
39
|
+
: AnyRouteModules;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/types/route-module.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
import type { Func } from './utils';
|
|
4
|
+
|
|
5
|
+
export type RouteModule = {
|
|
6
|
+
meta?: Func;
|
|
7
|
+
links?: Func;
|
|
8
|
+
headers?: Func;
|
|
9
|
+
loader?: Func;
|
|
10
|
+
clientLoader?: Func;
|
|
11
|
+
action?: Func;
|
|
12
|
+
clientAction?: Func;
|
|
13
|
+
HydrateFallback?: Func;
|
|
14
|
+
default?: Func;
|
|
15
|
+
ErrorBoundary?: Func;
|
|
16
|
+
[key: string]: unknown; // allow user-defined exports
|
|
17
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Vendored from react-router@7.18.1 packages/react-router/lib/types/utils.ts — unmodified.
|
|
2
|
+
// Re-vendor with `node scripts/vendor-remix-router.mjs`; never hand-edit.
|
|
3
|
+
export type Expect<T extends true> = T;
|
|
4
|
+
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
export type Equal<X, Y> =
|
|
7
|
+
(<T>() => T extends X ? 1 : 2) extends
|
|
8
|
+
(<T>() => T extends Y ? 1 : 2) ? true : false
|
|
9
|
+
|
|
10
|
+
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
11
|
+
|
|
12
|
+
export type Func = (...args: any[]) => unknown;
|
|
13
|
+
|
|
14
|
+
export type Pretty<T> = { [K in keyof T]: T[K] } & {};
|
|
15
|
+
|
|
16
|
+
// Emulates https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#improved-type-inference-for-object-literals
|
|
17
|
+
export type Normalize<T> = _Normalize<UnionKeys<T>, T>;
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
type _Normalize<Key extends keyof any, T> =
|
|
20
|
+
T extends infer U ?
|
|
21
|
+
Pretty<
|
|
22
|
+
& { [K in Key as K extends keyof U ? undefined extends U[K] ? never : K : never]: K extends keyof U ? U[K] : never }
|
|
23
|
+
& { [K in Key as K extends keyof U ? undefined extends U[K] ? K : never : never]?: K extends keyof U ? U[K] : never }
|
|
24
|
+
& { [K in Key as K extends keyof U ? never : K]?: undefined}
|
|
25
|
+
>
|
|
26
|
+
:
|
|
27
|
+
never
|
|
28
|
+
type UnionKeys<T> = T extends any ? keyof T : never;
|
|
29
|
+
|
|
30
|
+
// prettier-ignore
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
32
|
+
type __tests = [
|
|
33
|
+
Expect<Equal<Normalize<{}>, {}>>,
|
|
34
|
+
Expect<Equal<Normalize<{a: string}>, {a: string}>>,
|
|
35
|
+
Expect<Equal<Normalize<{a: string} | {b: string}>, {a: string, b?: undefined} | {a?: undefined , b: string}>>,
|
|
36
|
+
Expect<Equal<Normalize<{a?: string} | {b?: string}>, {a?: string, b?: undefined} | {a?: undefined , b?: string}>>,
|
|
37
|
+
]
|