feat: add TypeScript type safety with strategic any assertions (v2.17.5)

Added comprehensive TypeScript type definitions for n8n node parsing while
maintaining zero compilation errors. Uses pragmatic "70% benefit with 0%
breakage" approach with strategic `any` assertions.

## Type Definitions (src/types/node-types.ts)
- NodeClass union type replaces `any` in method signatures
- Type guards: isVersionedNodeInstance(), isVersionedNodeClass()
- Utility functions for safe node handling

## Parser Updates
- node-parser.ts: All methods use NodeClass (15+ methods)
- simple-parser.ts: Strongly typed method signatures
- property-extractor.ts: Typed extraction methods
- 30+ method signatures improved

## Strategic Pattern
- Strong types in public method signatures (caller type safety)
- Strategic `as any` assertions for internal union type access
- Pattern: const desc = description as any; // Access union properties

## Benefits
- Better IDE support and auto-complete
- Compile-time safety at call sites
- Type-based documentation
- Zero compilation errors
- Bug prevention (would have caught v2.17.4 baseDescription issue)

## Test Updates
- All test files updated with `as any` for mock objects
- Zero compilation errors maintained

## Known Limitations
- ~70% type coverage (signatures typed, internal logic uses assertions)
- Union types (INodeTypeBaseDescription vs INodeTypeDescription) not fully resolved
- Future work: Conditional types or overloads for 100% type safety

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-07 22:16:59 +02:00
parent 8e2e1dce62
commit f3164e202f
15 changed files with 1293 additions and 314 deletions

View File

@@ -41,7 +41,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(outputs);
expect(result.outputNames).toBeUndefined();
@@ -60,7 +60,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputNames).toEqual(outputNames);
expect(result.outputs).toBeUndefined();
@@ -84,7 +84,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(outputs);
expect(result.outputNames).toEqual(outputNames);
@@ -103,7 +103,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual([singleOutput]);
});
@@ -119,7 +119,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputNames).toEqual(['main']);
});
@@ -152,7 +152,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
// Should get outputs from latest version (2)
expect(result.outputs).toEqual(versionedOutputs);
@@ -172,7 +172,7 @@ describe('NodeParser - Output Extraction', () => {
}
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toBeUndefined();
expect(result.outputNames).toBeUndefined();
@@ -189,7 +189,7 @@ describe('NodeParser - Output Extraction', () => {
description = nodeDescription;
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toBeUndefined();
expect(result.outputNames).toBeUndefined();
@@ -229,7 +229,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
// Should use latest version (3)
expect(result.outputs).toEqual([
@@ -259,7 +259,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(baseOutputs);
});
@@ -279,7 +279,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(ifOutputs);
expect(result.outputNames).toEqual(['true', 'false']);
@@ -300,7 +300,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(splitInBatchesOutputs);
expect(result.outputNames).toEqual(['done', 'loop']);
@@ -331,7 +331,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(switchOutputs);
expect(result.outputNames).toEqual(['0', '1', '2', 'fallback']);
@@ -347,7 +347,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual([]);
expect(result.outputNames).toEqual([]);
@@ -369,7 +369,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toEqual(outputs);
expect(result.outputNames).toEqual(outputNames);
@@ -405,7 +405,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toHaveLength(2);
expect(result.outputs).toBeDefined();
@@ -442,7 +442,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toHaveLength(2);
expect(result.outputs).toBeDefined();
@@ -464,7 +464,7 @@ describe('NodeParser - Output Extraction', () => {
};
};
const result = parser.parse(NodeClass, 'n8n-nodes-base');
const result = parser.parse(NodeClass as any, 'n8n-nodes-base');
expect(result.outputs).toBeUndefined();
expect(result.outputNames).toBeUndefined();