docs: json,typescript,types (2 files)

This commit is contained in:
Richard Ward
2025-12-31 12:31:40 +00:00
parent 5dad01c4fe
commit dcdb52de6f
2 changed files with 449 additions and 0 deletions

241
TYPESCRIPT_JSON_PATTERN.md Normal file
View File

@@ -0,0 +1,241 @@
# TypeScript JSON Abstraction Pattern
## Concept Overview
Just as we've abstracted CSS into declarative JSON schemas (`styles.json`), this demonstrates how TypeScript type definitions can be represented in a similar JSON format.
## Pattern Comparison
### CSS Pattern (styles.json V2)
```
Declarative JSON → StylesCompiler → CSS Output
```
### TypeScript Pattern (types.json)
```
Declarative JSON → TypeScriptCompiler → .d.ts Output
```
## Architecture
### 1. Schema Structure
**File Location**: `packages/{package}/seed/types.json`
**Core Sections**:
- **tokens**: Reusable primitive type aliases
- **interfaces**: Object type definitions
- **types**: Union types, literal types
- **generics**: Generic type definitions
- **rules**: Type constraints and modifiers
- **exports**: Export configuration
- **environments**: Target environments (ESM, CJS, TypeScript)
- **composition**: Type intersections and combinations
### 2. Benefits of This Approach
1. **Non-Developer Friendly**: JSON is easier to read/edit than TypeScript syntax
2. **Composable**: Build complex types from simple building blocks
3. **Multi-Target**: Single source generates multiple output formats
4. **Versionable**: Schema versioning allows evolution
5. **Debuggable**: Can build visualization tools (like StylesPanel)
6. **Consistent**: Mirrors the proven styles.json pattern
### 3. Example: From JSON to TypeScript
**Input** (`types.json`):
```json
{
"interfaces": [
{
"name": "ButtonProps",
"extends": ["BaseComponentProps"],
"properties": {
"variant": {
"type": "'primary' | 'secondary'",
"optional": false
},
"size": {
"type": "'small' | 'medium' | 'large'",
"default": "'medium'"
}
}
}
]
}
```
**Output** (compiled TypeScript):
```typescript
export interface ButtonProps extends BaseComponentProps {
variant: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
}
```
### 4. Compiler Implementation Sketch
```typescript
class TypeScriptCompiler {
private schema: TypeScriptSchema;
compile(): string {
const output: string[] = [];
// Compile tokens
if (this.schema.tokens) {
output.push(this.compileTokens(this.schema.tokens));
}
// Compile interfaces
if (this.schema.interfaces) {
output.push(...this.schema.interfaces.map(i => this.compileInterface(i)));
}
// Compile types
if (this.schema.types) {
output.push(...this.schema.types.map(t => this.compileType(t)));
}
// Apply rules/modifiers
return this.applyRules(output.join('\n\n'));
}
private compileInterface(def: InterfaceDefinition): string {
const ext = def.extends?.length ? ` extends ${def.extends.join(', ')}` : '';
const props = Object.entries(def.properties)
.map(([key, prop]) => {
const optional = prop.optional ? '?' : '';
const readonly = prop.readonly ? 'readonly ' : '';
return ` ${readonly}${key}${optional}: ${prop.type};`;
})
.join('\n');
return `export interface ${def.name}${ext} {\n${props}\n}`;
}
private compileType(def: TypeDefinition): string {
if (def.kind === 'union') {
return `export type ${def.name} = ${def.values.join(' | ')};`;
}
return `export type ${def.name} = ${def.definition};`;
}
}
```
### 5. Integration Points
Following the styles.json pattern:
```typescript
// Similar to loadPackageStyles()
async function loadPackageTypes(packageId: string): Promise<string> {
const response = await fetch(`/packages/${packageId}/seed/types.json`);
const schema = await response.json();
const compiler = new TypeScriptCompiler(schema);
return compiler.compile();
}
// Generate .d.ts files at build time
function generateTypeDefinitions() {
const packages = getPackageList();
for (const pkg of packages) {
const typeSchema = loadTypeSchema(pkg);
const compiler = new TypeScriptCompiler(typeSchema);
const output = compiler.compile();
fs.writeFileSync(
`packages/${pkg}/dist/types.d.ts`,
output
);
}
}
```
### 6. Advanced Features
**Composition** (like styles.json appearance layers):
```json
{
"composition": [
{
"name": "EnhancedButtonProps",
"kind": "intersection",
"types": [
"ButtonProps",
"AccessibilityProps",
"AnalyticsProps"
]
}
]
}
```
**Rules** (like styles.json selector rules):
```json
{
"rules": [
{
"selector": {
"interfaceName": "UserData",
"property": "id"
},
"modifiers": ["readonly", "required"],
"priority": 10
}
]
}
```
**Environments** (like styles.json responsive environments):
```json
{
"environments": {
"node": {
"moduleSystem": "CommonJS",
"globals": ["process", "Buffer"]
},
"browser": {
"moduleSystem": "ES2015",
"globals": ["window", "document"]
}
}
}
```
### 7. Practical Use Cases
1. **Component Libraries**: Define all component prop types in JSON
2. **API Contracts**: Generate TypeScript types from API schemas
3. **Configuration**: Type-safe config objects defined declaratively
4. **Theme Systems**: Type definitions for theme tokens and variants
5. **State Management**: Action and state type definitions
### 8. Future Enhancements
- **Validation**: JSON Schema validation for types.json
- **Visualization**: Storybook panel to explore type relationships (like StylesPanel)
- **Code Generation**: Generate not just types but implementation stubs
- **Documentation**: Auto-generate docs from type definitions
- **Migration Tools**: Convert existing .ts files to types.json
## Example Files
See the demonstration in:
- [packages/shared/seed/types.json](packages/shared/seed/types.json)
This mirrors the existing pattern:
- [packages/shared/seed/styles.json](packages/shared/seed/styles.json)
## Conclusion
This pattern demonstrates that **any code abstraction can be represented declaratively in JSON** when you:
1. Define a clear schema
2. Build a compiler to transform it
3. Support composition and rules
4. Enable multi-target output
5. Provide tooling for debugging/visualization
The same pattern used for CSS → styles.json applies perfectly to TypeScript → types.json.

View File

@@ -0,0 +1,208 @@
{
"schema_version": "1.0.0",
"package": "shared",
"description": "Type definitions abstraction - similar pattern to styles.json V2",
"tokens": {
"primitives": {
"StringType": "string",
"NumberType": "number",
"BooleanType": "boolean",
"VoidType": "void",
"UnknownType": "unknown",
"NeverType": "never"
},
"common": {
"ID": "string",
"Timestamp": "number",
"JSONValue": "string | number | boolean | null | JSONArray | JSONObject",
"Callback": "(event: any) => void",
"AsyncCallback": "(event: any) => Promise<void>"
}
},
"interfaces": [
{
"id": "base_component_props",
"name": "BaseComponentProps",
"description": "Base props for all components",
"extends": [],
"properties": {
"id": {
"type": "string",
"optional": true,
"description": "Unique identifier"
},
"className": {
"type": "string",
"optional": true,
"description": "CSS class name"
},
"style": {
"type": "React.CSSProperties",
"optional": true,
"description": "Inline styles"
},
"children": {
"type": "React.ReactNode",
"optional": true,
"description": "Child elements"
}
}
},
{
"id": "theme_config",
"name": "ThemeConfig",
"description": "Theme configuration object",
"properties": {
"mode": {
"type": "'light' | 'dark'",
"optional": false,
"default": "'light'"
},
"primaryColor": {
"type": "string",
"optional": true,
"description": "Primary theme color"
},
"spacing": {
"type": "number",
"optional": true,
"default": 8,
"description": "Base spacing unit in pixels"
}
}
}
],
"types": [
{
"id": "component_type_enum",
"name": "ComponentType",
"kind": "union",
"values": [
"'div'",
"'span'",
"'button'",
"'input'",
"'section'",
"'header'",
"'footer'"
]
},
{
"id": "size_variant",
"name": "SizeVariant",
"kind": "union",
"values": ["'small'", "'medium'", "'large'"]
},
{
"id": "color_variant",
"name": "ColorVariant",
"kind": "union",
"values": ["'primary'", "'secondary'", "'success'", "'error'", "'warning'"]
}
],
"generics": [
{
"id": "generic_props",
"name": "PropsWithType",
"typeParams": ["T"],
"definition": "BaseComponentProps & { type: T }"
},
{
"id": "generic_state",
"name": "StateWithData",
"typeParams": ["D"],
"definition": "{ loading: boolean; error?: string; data?: D }"
}
],
"rules": [
{
"id": "readonly_id_rule",
"selector": {
"interfaceName": "BaseComponentProps",
"property": "id"
},
"modifiers": ["readonly"],
"enabled": true,
"priority": 10
},
{
"id": "required_theme_mode",
"selector": {
"interfaceName": "ThemeConfig",
"property": "mode"
},
"modifiers": ["required"],
"enabled": true,
"priority": 20
}
],
"exports": [
{
"id": "export_base_props",
"name": "BaseComponentProps",
"kind": "interface",
"environments": ["esm", "cjs", "types"],
"default": false
},
{
"id": "export_theme_config",
"name": "ThemeConfig",
"kind": "interface",
"environments": ["esm", "cjs", "types"],
"default": false
},
{
"id": "export_component_type",
"name": "ComponentType",
"kind": "type",
"environments": ["esm", "cjs", "types"],
"default": false
},
{
"id": "export_size_variant",
"name": "SizeVariant",
"kind": "type",
"environments": ["esm", "cjs", "types"],
"default": false
}
],
"environments": {
"esm": {
"moduleSystem": "ES2015",
"target": "ES2020"
},
"cjs": {
"moduleSystem": "CommonJS",
"target": "ES2015"
},
"types": {
"moduleSystem": "ES2015",
"target": "ESNext",
"declarationOnly": true
}
},
"composition": [
{
"id": "button_props_composition",
"name": "ButtonProps",
"kind": "intersection",
"types": [
"BaseComponentProps",
{
"size": "SizeVariant",
"variant": "ColorVariant",
"disabled": "boolean",
"onClick": "Callback"
}
]
}
]
}