-
Notifications
You must be signed in to change notification settings - Fork 9
Fix/record mapped type schemas #1880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 9 files
Prompt for AI agents (all 1 issues)
Understand the root cause of the following 1 issues and fix them.
<file name="packages/runner/src/harness/engine.ts">
<violation number="1" location="packages/runner/src/harness/engine.ts:257">
hasDefaultExport misses default-exported function/class declarations, so files with `export default function` lose their default export when re-mapped and Engine.run fails.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
|
|
||
| // Checks if a source file has a default export by parsing its AST. | ||
| // Detects both `export default ...` and `export { ... as default }` patterns. | ||
| function hasDefaultExport(source: Source): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasDefaultExport misses default-exported function/class declarations, so files with export default function lose their default export when re-mapped and Engine.run fails.
Prompt for AI agents
Address the following comment on packages/runner/src/harness/engine.ts at line 257:
<comment>hasDefaultExport misses default-exported function/class declarations, so files with `export default function` lose their default export when re-mapped and Engine.run fails.</comment>
<file context>
@@ -251,6 +252,36 @@ function computeId(program: Program): string {
+// Checks if a source file has a default export by parsing its AST.
+// Detects both `export default ...` and `export { ... as default }` patterns.
+function hasDefaultExport(source: Source): boolean {
+ const sourceFile = ts.createSourceFile(
+ source.name,
</file context>
✅ Addressed in 5f0a842
58f010c to
25ff11e
Compare
| ] | ||
| } | ||
| }, | ||
| "required": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if we should have "additionalProperties": false on these. In the TypeScript world, we don't let you assign an object with extra properties to these.
Edit: this isn't meant to be something that should be changed -- just not sure what the best behavior is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was a cool question that caused me to learn some things about typescript, thank you :)
here's an amalgamation of some inputs from claude that make sense to me as a set of takeaways:
Arguments for "additionalProperties": false:
- Stricter validation matches TypeScript's intent
- Catches accidental extra properties (typos, outdated fields)
- More defensive/explicit about the contract
Arguments against (current behavior):
- TypeScript's excess property checking only applies to object literals in direct assignments
type Settings = Record<"theme" | "language", string>;
const valid: Settings = { theme: "dark", language: "en" }; // ✓
const extra: Settings = { theme: "dark", language: "en", foo: "x" }; // ✗ TypeScript error
// ...but this is valid TypeScript:
const obj = { theme: "dark", language: "en", foo: "x" };
const settings: Settings = obj; // ✓ No error! TypeScript's structural typing
- JSON Schema is often used for runtime validation where data comes from external sources
- Being more permissive at runtime matches TypeScript's structural nature
- The schema generator has been following a pattern of only adding additionalProperties: false when TypeScript explicitly forbids extra properties (not just excess
property checking)
What do you think the validation use case is here? Are these schemas primarily for:
- Type validation at API boundaries (stricter might be better)
- Internal type checking (current behavior is fine)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merging this for now but happy to keep chatting about this topic and can easily make whatever changes we decide on in a follow-up!
Mapped types (like Record<K, V> and inline mapped types) have synthetic properties without explicit declarations. Updated safeGetPropertyType() to use checker.getPropertyOfType() + getTypeOfSymbol() to resolve these property types correctly, preventing them from falling back to 'any' (which generates permissive 'true' schemas). Added fixture test for inline mapped types to prevent regression. Note: record-mapped-types.test.ts is included but currently fails due to Record<K,V> being incorrectly hoisted to $defs. This will be fixed in the next commit (Phase 2: native type detection). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implemented lib type detection to prevent built-in TypeScript mapped types (Record, Partial, Pick, Required, etc.) from being hoisted to $defs. These types are defined in TypeScript's lib files and should be inlined rather than referenced. Detection strategy: - Check if type symbol/alias/target declarations come from lib files - Match patterns: lib.*.d.ts, ES*.d.ts, or /typescript/*.d.ts paths - Check symbol, target symbol (for type references), and alias symbol Added fixture tests for Record<K, V> to prevent regression. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…re parametrized generics rather than whether they're library functions
Cleanup:
- Reverted aliasSymbol extraction to keep diff minimal
- Reverted targetSymbol scope to original tighter scope
- Both changes maintain exact same behavior, just cleaner
New edge case tests:
- Record<string, T> with additionalProperties (passes)
- Generic types with default parameters (FAILING - needs investigation)
- Conditional types resolving to Records (FAILING - needs investigation)
Investigation notes for next session:
- Box<number> is generating {value: string} instead of {value: number}
- Conditional MaybeRecord<"foo"|"bar"> generates anyOf (may be correct due to distributive conditionals)
- Need to debug why Box<number> loses its type argument
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…eGetPropertyType - Compare parent vs declaration types to detect generic instantiation (e.g., Box<number>) - Strip | undefined from optional properties using type flags instead of string matching - Handle Partial<T> and other mapped types correctly - Fix recursion tests by preferring declaration type for optional properties
0a52b71 to
f68116e
Compare
Not ready for review
Builds on fix/detect-actual-default-exports
Summary by cubic
Fix schema generation for mapped types (Record and inline mapped types) and correct default-export detection in the runner to prevent invalid re-exports and permissive schemas.
Bug Fixes
Dependencies