feat(types)!: enforce strict type narrowing for Link and Script unions#729
Conversation
Remove GenericLink from Link union and GenericScript from Script union so TypeScript enforces per-rel and per-type constraints. For example, rel="preload" + as="font" now requires crossorigin, and rel="mask-icon" requires color. Also makes meta content required (accepts null for removal) and allows innerHTML as an alternative to textContent in InlineScript. GenericLink and GenericScript are still exported for explicit use with custom rel/type values via `satisfies`.
📝 WalkthroughWalkthroughThe PR tightens schema types (making meta Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bundle Size Analysis
|
Parser builds scripts from HTML attributes (index signature) and useScript creates defaults incrementally; both need looser types since they don't construct complete discriminated union members.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/unhead/src/parser/index.ts (1)
512-512: Replaceas anywith a more specific type for parsed scripts.Line 512 uses
as anyto cast dynamically-assembled script attributes from HTML parsing. This removes type guarantees and risks masking invalid payloads. Useas GenericScriptinstead, which is explicitly documented as the escape hatch for custom/non-standard script types and provides narrower type safety thanany.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/unhead/src/parser/index.ts` at line 512, Replace the unsafe cast "as any" on the parsed script attributes with the documented narrower escape hatch type GenericScript: in the code that pushes parsed scripts (the statement using input.script and the variable scriptAttrs) change the cast to use GenericScript so the pushed values are typed; also ensure GenericScript is imported or exported where needed and that the input.script array type can accept GenericScript to preserve type safety across the parser functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/unhead/src/parser/index.ts`:
- Line 512: Replace the unsafe cast "as any" on the parsed script attributes
with the documented narrower escape hatch type GenericScript: in the code that
pushes parsed scripts (the statement using input.script and the variable
scriptAttrs) change the cast to use GenericScript so the pushed values are
typed; also ensure GenericScript is imported or exported where needed and that
the input.script array type can accept GenericScript to preserve type safety
across the parser functions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 04573f5c-f4c1-45a3-8f63-220185a4ac06
📒 Files selected for processing (2)
packages/unhead/src/parser/index.tspackages/unhead/src/scripts/useScript.ts
✅ Files skipped from review due to trivial changes (1)
- packages/unhead/src/scripts/useScript.ts
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
❓ Type of change
📚 Description
The
GenericLinkandGenericScriptfallback types silently absorbed all knownrel/typevalues in theLinkandScriptdiscriminated unions, preventing TypeScript from enforcing per-type constraints. For example,{ rel: 'preload', as: 'font', href: '...' }compiled without error even thoughPreloadFontLinkrequirescrossorigin.This PR removes the generic fallbacks from the unions so the type system enforces:
rel="preload" + as="font"requirescrossoriginrel="mask-icon"requirescolorsrc, external scripts can't havetextContentcontentis required (usenullexplicitly for removal)GenericLinkandGenericScriptremain exported for explicit use with custom values viasatisfies.Linkunion no longer includesGenericLink(customrelvalues need explicit typing)Scriptunion no longer includesGenericScript(customtypevalues need explicit typing)InlineScript/InlineModuleScriptchanged from interface to type (now acceptsinnerHTMLas alternative totextContent)contentis now required inUnheadMeta(usecontent: nullfor removal instead of omitting)📝 Migration
Summary by CodeRabbit
Breaking Changes
contentproperty (usenullto remove tags).relvalues must be explicitly typed/handled.textContentorinnerHTML(mutually exclusive).Tests