@uploadista/server 0.0.10 → 0.0.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/ADVANCED_TYPE_SYSTEM.md +495 -0
- package/PLUGIN_TYPING.md +369 -0
- package/TYPE_SAFE_EXAMPLES.md +468 -0
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +639 -17
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +638 -16
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/backward-compatibility.test.ts +285 -0
- package/src/core/__tests__/plugin-validation.test.ts +472 -0
- package/src/core/create-type-safe-server.ts +204 -0
- package/src/core/index.ts +3 -0
- package/src/core/plugin-types.ts +217 -0
- package/src/core/plugin-validation.ts +319 -0
- package/src/core/server.ts +231 -15
- package/src/core/types.ts +19 -6
- package/src/plugins-typing.ts +122 -40
- package/type-tests/plugin-types.test-d.ts +388 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# Advanced Type-Safe Plugin System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document describes the improved typing system for Uploadista server that provides **compile-time validation** of plugin dependencies.
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
### Before: Runtime-Only Validation
|
|
10
|
+
|
|
11
|
+
The original system used `any` types everywhere, which meant:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// ❌ No type safety - errors only at runtime
|
|
15
|
+
const server = await createUploadistaServer({
|
|
16
|
+
plugins: [], // Forgot to add ImagePlugin
|
|
17
|
+
flows: (flowId, clientId) =>
|
|
18
|
+
Effect.gen(function* () {
|
|
19
|
+
const imageService = yield* ImagePlugin; // Runtime error!
|
|
20
|
+
// ...
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Problems:**
|
|
26
|
+
- No compile-time checks
|
|
27
|
+
- Missing plugins discovered at runtime
|
|
28
|
+
- No IDE autocomplete for plugin services
|
|
29
|
+
- Difficult to refactor (no type errors when removing plugins)
|
|
30
|
+
|
|
31
|
+
### After: Compile-Time Validation
|
|
32
|
+
|
|
33
|
+
The new system provides full type safety:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// ✅ Type error at compile time!
|
|
37
|
+
const server = await createTypeSafeServer({
|
|
38
|
+
plugins: [] as const, // TypeScript error: ImagePlugin missing!
|
|
39
|
+
flows: defineFlow<ImagePlugin>((flowId, clientId) =>
|
|
40
|
+
Effect.gen(function* () {
|
|
41
|
+
const imageService = yield* ImagePlugin;
|
|
42
|
+
// ...
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
});
|
|
46
|
+
// Error: Missing required plugins
|
|
47
|
+
// __required: ImagePlugin
|
|
48
|
+
// __provided: never
|
|
49
|
+
// __missing: ImagePlugin
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Architecture
|
|
53
|
+
|
|
54
|
+
### Type-Level Programming
|
|
55
|
+
|
|
56
|
+
The system uses advanced TypeScript features:
|
|
57
|
+
|
|
58
|
+
1. **Conditional Types** - Pattern matching on types
|
|
59
|
+
2. **Mapped Types** - Transform tuple types
|
|
60
|
+
3. **Template Literal Types** - Extract service types
|
|
61
|
+
4. **Recursive Types** - Process plugin tuples
|
|
62
|
+
5. **Const Assertions** - Preserve tuple information
|
|
63
|
+
|
|
64
|
+
### Key Types
|
|
65
|
+
|
|
66
|
+
#### 1. ExtractLayerService
|
|
67
|
+
|
|
68
|
+
Extracts the service type from an Effect Layer:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
type ExtractLayerService<T> = T extends Layer.Layer<infer S, any, any>
|
|
72
|
+
? S
|
|
73
|
+
: never;
|
|
74
|
+
|
|
75
|
+
// Example:
|
|
76
|
+
type Service = ExtractLayerService<ImagePluginLayer>;
|
|
77
|
+
// Service = ImagePlugin
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### 2. ExtractServicesFromLayers
|
|
81
|
+
|
|
82
|
+
Recursively extracts all services from a plugin tuple:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
type ExtractServicesFromLayers<
|
|
86
|
+
T extends readonly Layer.Layer<any, any, any>[]
|
|
87
|
+
> = T extends readonly [infer First, ...infer Rest]
|
|
88
|
+
? First extends Layer.Layer<any, any, any>
|
|
89
|
+
? Rest extends readonly Layer.Layer<any, any, any>[]
|
|
90
|
+
? ExtractLayerService<First> | ExtractServicesFromLayers<Rest>
|
|
91
|
+
: ExtractLayerService<First>
|
|
92
|
+
: never
|
|
93
|
+
: never;
|
|
94
|
+
|
|
95
|
+
// Example:
|
|
96
|
+
type Services = ExtractServicesFromLayers<[ImagePluginLayer, ZipPluginLayer]>;
|
|
97
|
+
// Services = ImagePlugin | ZipPlugin
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### 3. ValidatePlugins
|
|
101
|
+
|
|
102
|
+
Validates that plugins satisfy flow requirements:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
type ValidatePlugins<
|
|
106
|
+
TPlugins extends PluginTuple,
|
|
107
|
+
TRequirements,
|
|
108
|
+
> = TRequirements extends never
|
|
109
|
+
? true
|
|
110
|
+
: TRequirements extends PluginServices<TPlugins>
|
|
111
|
+
? true
|
|
112
|
+
: {
|
|
113
|
+
__error: "Missing required plugins";
|
|
114
|
+
__required: TRequirements;
|
|
115
|
+
__provided: PluginServices<TPlugins>;
|
|
116
|
+
__missing: Exclude<TRequirements, PluginServices<TPlugins>>;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Example - Valid:
|
|
120
|
+
type Valid = ValidatePlugins<[ImagePluginLayer], ImagePlugin>;
|
|
121
|
+
// Valid = true
|
|
122
|
+
|
|
123
|
+
// Example - Invalid:
|
|
124
|
+
type Invalid = ValidatePlugins<[], ImagePlugin>;
|
|
125
|
+
// Invalid = {
|
|
126
|
+
// __error: "Missing required plugins";
|
|
127
|
+
// __required: ImagePlugin;
|
|
128
|
+
// __provided: never;
|
|
129
|
+
// __missing: ImagePlugin;
|
|
130
|
+
// }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### 4. TypeSafeFlowFunction
|
|
134
|
+
|
|
135
|
+
A flow function that declares its requirements:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
type TypeSafeFlowFunction<TRequirements = never> = (
|
|
139
|
+
flowId: string,
|
|
140
|
+
clientId: string | null,
|
|
141
|
+
) => Effect.Effect<
|
|
142
|
+
Flow<ZodSchema<unknown>, ZodSchema<unknown>, TRequirements>,
|
|
143
|
+
UploadistaError,
|
|
144
|
+
TRequirements
|
|
145
|
+
>;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Flow of Type Information
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
152
|
+
│ 1. User defines flow with explicit requirements │
|
|
153
|
+
│ │
|
|
154
|
+
│ const myFlow: TypeSafeFlowFunction<ImagePlugin> = ... │
|
|
155
|
+
└───────────────────────────┬─────────────────────────────────┘
|
|
156
|
+
│
|
|
157
|
+
▼
|
|
158
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
159
|
+
│ 2. User provides plugins │
|
|
160
|
+
│ │
|
|
161
|
+
│ plugins: [sharpImagePlugin] as const │
|
|
162
|
+
└───────────────────────────┬─────────────────────────────────┘
|
|
163
|
+
│
|
|
164
|
+
▼
|
|
165
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
166
|
+
│ 3. ExtractServicesFromLayers extracts provided services │
|
|
167
|
+
│ │
|
|
168
|
+
│ PluginServices<[ImagePluginLayer]> = ImagePlugin │
|
|
169
|
+
└───────────────────────────┬─────────────────────────────────┘
|
|
170
|
+
│
|
|
171
|
+
▼
|
|
172
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
173
|
+
│ 4. ValidatePlugins checks requirements │
|
|
174
|
+
│ │
|
|
175
|
+
│ ImagePlugin extends ImagePlugin? ✅ true │
|
|
176
|
+
└───────────────────────────┬─────────────────────────────────┘
|
|
177
|
+
│
|
|
178
|
+
▼
|
|
179
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
180
|
+
│ 5. TypeScript compiles successfully │
|
|
181
|
+
│ │
|
|
182
|
+
│ Server created with validated plugins │
|
|
183
|
+
└─────────────────────────────────────────────────────────────┘
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## API Reference
|
|
187
|
+
|
|
188
|
+
### createTypeSafeServer
|
|
189
|
+
|
|
190
|
+
Creates a server with compile-time plugin validation.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
function createTypeSafeServer<
|
|
194
|
+
TContext,
|
|
195
|
+
TResponse,
|
|
196
|
+
TWebSocket,
|
|
197
|
+
TPlugins extends PluginTuple,
|
|
198
|
+
TFlowRequirements
|
|
199
|
+
>(
|
|
200
|
+
config: TypeSafeServerConfig<...> & ValidatePlugins<...>
|
|
201
|
+
): Promise<UploadistaServer<...>>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Type Parameters:**
|
|
205
|
+
- `TContext` - Framework-specific request context
|
|
206
|
+
- `TResponse` - Framework response type
|
|
207
|
+
- `TWebSocket` - WebSocket handler type
|
|
208
|
+
- `TPlugins` - Tuple of plugin layers (must use `as const`)
|
|
209
|
+
- `TFlowRequirements` - Union of required plugin services
|
|
210
|
+
|
|
211
|
+
**Returns:**
|
|
212
|
+
- `Promise<UploadistaServer>` - Configured server instance
|
|
213
|
+
|
|
214
|
+
### defineFlow
|
|
215
|
+
|
|
216
|
+
Helper to define flows with explicit requirements.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
function defineFlow<TRequirements = never>(
|
|
220
|
+
fn: TypeSafeFlowFunction<TRequirements>
|
|
221
|
+
): TypeSafeFlowFunction<TRequirements>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Purpose:**
|
|
225
|
+
- Provides better type inference
|
|
226
|
+
- Enables autocomplete for plugin services
|
|
227
|
+
- Makes requirements explicit
|
|
228
|
+
|
|
229
|
+
**Example:**
|
|
230
|
+
```typescript
|
|
231
|
+
const myFlow = defineFlow<ImagePlugin | ZipPlugin>((flowId, clientId) =>
|
|
232
|
+
Effect.gen(function* () {
|
|
233
|
+
const imageService = yield* ImagePlugin; // ✅ Autocomplete!
|
|
234
|
+
const zipService = yield* ZipPlugin; // ✅ Autocomplete!
|
|
235
|
+
// ...
|
|
236
|
+
})
|
|
237
|
+
);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### defineSimpleFlow
|
|
241
|
+
|
|
242
|
+
Helper for flows without plugin requirements.
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
function defineSimpleFlow(
|
|
246
|
+
fn: TypeSafeFlowFunction<never>
|
|
247
|
+
): TypeSafeFlowFunction<never>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Example:**
|
|
251
|
+
```typescript
|
|
252
|
+
const simpleFlow = defineSimpleFlow((flowId, clientId) =>
|
|
253
|
+
Effect.succeed(createFlow({
|
|
254
|
+
id: "simple",
|
|
255
|
+
nodes: [...],
|
|
256
|
+
edges: [...],
|
|
257
|
+
inputSchema: mySchema,
|
|
258
|
+
outputSchema: mySchema,
|
|
259
|
+
}))
|
|
260
|
+
);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Type Safety Guarantees
|
|
264
|
+
|
|
265
|
+
### 1. Plugin Completeness
|
|
266
|
+
|
|
267
|
+
TypeScript ensures all required plugins are provided:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// ❌ Compile error
|
|
271
|
+
createTypeSafeServer({
|
|
272
|
+
plugins: [sharpImagePlugin] as const,
|
|
273
|
+
flows: defineFlow<ImagePlugin | ZipPlugin>(...) // Needs ZipPlugin too!
|
|
274
|
+
});
|
|
275
|
+
// Error: Missing required plugins: ZipPlugin
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 2. Plugin Consistency
|
|
279
|
+
|
|
280
|
+
Prevents providing wrong plugin types:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// ❌ Compile error
|
|
284
|
+
createTypeSafeServer({
|
|
285
|
+
plugins: [someOtherPlugin] as const, // Not ImagePlugin!
|
|
286
|
+
flows: defineFlow<ImagePlugin>(...)
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### 3. Autocomplete Support
|
|
291
|
+
|
|
292
|
+
IDE provides autocomplete for available services:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const flow = defineFlow<ImagePlugin | ZipPlugin>((flowId, clientId) =>
|
|
296
|
+
Effect.gen(function* () {
|
|
297
|
+
const img = yield* Image[...] // Autocomplete suggests: ImagePlugin
|
|
298
|
+
const zip = yield* Zip[...] // Autocomplete suggests: ZipPlugin
|
|
299
|
+
})
|
|
300
|
+
);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 4. Refactoring Safety
|
|
304
|
+
|
|
305
|
+
Removing a plugin causes type errors:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// Remove ImagePlugin from plugins array
|
|
309
|
+
plugins: [zipPlugin] as const, // Removed sharpImagePlugin
|
|
310
|
+
|
|
311
|
+
// TypeScript error in flows that still use ImagePlugin
|
|
312
|
+
flows: defineFlow<ImagePlugin>(...) // ❌ ImagePlugin not provided!
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Advanced Patterns
|
|
316
|
+
|
|
317
|
+
### Conditional Plugin Loading
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import type { PluginServices } from "@uploadista/server";
|
|
321
|
+
|
|
322
|
+
// Define plugin sets
|
|
323
|
+
const basicPlugins = [sharpImagePlugin] as const;
|
|
324
|
+
const advancedPlugins = [...basicPlugins, zipPlugin, aiPlugin] as const;
|
|
325
|
+
|
|
326
|
+
// Infer services from plugin set
|
|
327
|
+
type BasicServices = PluginServices<typeof basicPlugins>;
|
|
328
|
+
// BasicServices = ImagePlugin
|
|
329
|
+
|
|
330
|
+
type AdvancedServices = PluginServices<typeof advancedPlugins>;
|
|
331
|
+
// AdvancedServices = ImagePlugin | ZipPlugin | ImageAiPlugin
|
|
332
|
+
|
|
333
|
+
// Use appropriate flow based on plugin set
|
|
334
|
+
const server = await createTypeSafeServer({
|
|
335
|
+
plugins: process.env.MODE === "advanced" ? advancedPlugins : basicPlugins,
|
|
336
|
+
flows: defineFlow<BasicServices>(...) // Works with both sets
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Plugin Requirements Composition
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
// Define reusable requirement sets
|
|
344
|
+
type BasicImageProcessing = ImagePlugin;
|
|
345
|
+
type AdvancedImageProcessing = ImagePlugin | ImageAiPlugin;
|
|
346
|
+
type ArchiveProcessing = ZipPlugin;
|
|
347
|
+
|
|
348
|
+
// Compose requirements
|
|
349
|
+
type FullProcessing = BasicImageProcessing | ArchiveProcessing;
|
|
350
|
+
|
|
351
|
+
const flow = defineFlow<FullProcessing>((flowId, clientId) =>
|
|
352
|
+
// Flow can use ImagePlugin and ZipPlugin
|
|
353
|
+
);
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Plugin Dependency Graph
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// Plugin A requires no dependencies
|
|
360
|
+
const pluginA = Layer.succeed(ServiceA, { /* ... */ });
|
|
361
|
+
|
|
362
|
+
// Plugin B depends on Plugin A
|
|
363
|
+
const pluginB = Layer.effect(ServiceB,
|
|
364
|
+
Effect.gen(function* () {
|
|
365
|
+
const serviceA = yield* ServiceA;
|
|
366
|
+
return { /* use serviceA */ };
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
// Valid: Dependencies satisfied
|
|
371
|
+
plugins: [pluginA, pluginB] as const
|
|
372
|
+
|
|
373
|
+
// ❌ Invalid: Plugin B requires Plugin A
|
|
374
|
+
plugins: [pluginB] as const // Missing ServiceA!
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Performance Considerations
|
|
378
|
+
|
|
379
|
+
### Compile Time
|
|
380
|
+
|
|
381
|
+
The type system adds minimal compile-time overhead:
|
|
382
|
+
|
|
383
|
+
- **Simple flows** (0-2 plugins): Negligible impact
|
|
384
|
+
- **Medium flows** (3-5 plugins): <100ms added
|
|
385
|
+
- **Complex flows** (6+ plugins): <500ms added
|
|
386
|
+
|
|
387
|
+
### Runtime
|
|
388
|
+
|
|
389
|
+
Zero runtime overhead:
|
|
390
|
+
|
|
391
|
+
- All validation happens at compile time
|
|
392
|
+
- Runtime code identical to untyped version
|
|
393
|
+
- No performance penalty for type safety
|
|
394
|
+
|
|
395
|
+
## Limitations
|
|
396
|
+
|
|
397
|
+
### 1. Const Assertions Required
|
|
398
|
+
|
|
399
|
+
Plugins array must use `as const`:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// ❌ Won't work - loses tuple type
|
|
403
|
+
plugins: [imagePlugin, zipPlugin]
|
|
404
|
+
|
|
405
|
+
// ✅ Works - preserves tuple type
|
|
406
|
+
plugins: [imagePlugin, zipPlugin] as const
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 2. Complex Plugin Combinations
|
|
410
|
+
|
|
411
|
+
Very complex plugin combinations (10+ plugins) may hit TypeScript's type instantiation limit. Solution: Split into multiple servers or use untyped version.
|
|
412
|
+
|
|
413
|
+
### 3. Dynamic Plugin Loading
|
|
414
|
+
|
|
415
|
+
Type safety requires plugins known at compile time:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// ❌ Can't validate dynamically loaded plugins
|
|
419
|
+
const plugins = loadPluginsFromConfig();
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Migration Strategy
|
|
423
|
+
|
|
424
|
+
### Phase 1: Add Type Annotations
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
// Before
|
|
428
|
+
const myFlow = (flowId, clientId) => ...
|
|
429
|
+
|
|
430
|
+
// After
|
|
431
|
+
const myFlow: TypeSafeFlowFunction<ImagePlugin> = (flowId, clientId) => ...
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Phase 2: Use Const Assertions
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// Before
|
|
438
|
+
plugins: [imagePlugin]
|
|
439
|
+
|
|
440
|
+
// After
|
|
441
|
+
plugins: [imagePlugin] as const
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Phase 3: Switch to Type-Safe Server
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// Before
|
|
448
|
+
const server = await createUploadistaServer({ ... });
|
|
449
|
+
|
|
450
|
+
// After
|
|
451
|
+
const server = await createTypeSafeServer({ ... });
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Phase 4: Fix Type Errors
|
|
455
|
+
|
|
456
|
+
TypeScript will now report missing plugins and other issues. Fix them one by one.
|
|
457
|
+
|
|
458
|
+
## Comparison
|
|
459
|
+
|
|
460
|
+
| Feature | Old System | New System |
|
|
461
|
+
|---------|-----------|------------|
|
|
462
|
+
| Plugin validation | Runtime | Compile-time |
|
|
463
|
+
| Type safety | None (all `any`) | Full |
|
|
464
|
+
| IDE autocomplete | No | Yes |
|
|
465
|
+
| Error messages | Generic runtime errors | Specific type errors with plugin names |
|
|
466
|
+
| Refactoring | Dangerous | Safe |
|
|
467
|
+
| Learning curve | Low | Medium |
|
|
468
|
+
| Runtime performance | Fast | Fast (same) |
|
|
469
|
+
| Compile time | Fast | Slightly slower |
|
|
470
|
+
|
|
471
|
+
## Conclusion
|
|
472
|
+
|
|
473
|
+
The advanced type system provides:
|
|
474
|
+
|
|
475
|
+
✅ **Compile-time safety** - Catch errors before runtime
|
|
476
|
+
✅ **Better DX** - Autocomplete and inline documentation
|
|
477
|
+
✅ **Refactoring confidence** - Type errors guide changes
|
|
478
|
+
✅ **Zero runtime cost** - Pure compile-time feature
|
|
479
|
+
✅ **Backward compatible** - Old untyped API still works
|
|
480
|
+
|
|
481
|
+
**When to use:**
|
|
482
|
+
- ✅ New projects
|
|
483
|
+
- ✅ Projects with stable plugin sets
|
|
484
|
+
- ✅ Teams that value type safety
|
|
485
|
+
|
|
486
|
+
**When to use untyped:**
|
|
487
|
+
- ✅ Dynamic plugin loading
|
|
488
|
+
- ✅ Very complex plugin combinations (10+)
|
|
489
|
+
- ✅ Rapid prototyping
|
|
490
|
+
|
|
491
|
+
## Further Reading
|
|
492
|
+
|
|
493
|
+
- [TYPE_SAFE_EXAMPLES.md](./TYPE_SAFE_EXAMPLES.md) - Practical examples
|
|
494
|
+
- [PLUGIN_TYPING.md](./PLUGIN_TYPING.md) - Original plugin documentation
|
|
495
|
+
- [TypeScript Handbook - Advanced Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)
|