Skip to content

fix(types): support union rel/type in defineLink and defineScript#765

Merged
harlan-zw merged 1 commit into
mainfrom
fix/link-rel-resource-hints-union
May 27, 2026
Merged

fix(types): support union rel/type in defineLink and defineScript#765
harlan-zw merged 1 commit into
mainfrom
fix/link-rel-resource-hints-union

Conversation

@harlan-zw
Copy link
Copy Markdown
Collaborator

@harlan-zw harlan-zw commented May 27, 2026

🔗 Linked issue

N/A

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

defineLink / defineScript previously rejected runtime-determined rels and types (e.g. cond ? 'preconnect' : 'dns-prefetch') because InferLink / InferScript only matched single-literal inputs against the discriminated union.

Distributes the match over the input union, then intersects matching variants (minus their discriminator) so optional fields like crossorigin carry over while incompatible required fields (e.g. preload's as, application/ld+json's textContent) still force the stricter shape. Single-literal inputs remain strictly narrowed.

Adversarial type-test coverage added for resource-hint unions, FaviconLink's internal rel union, generic-rel fallthrough, and script-type unions.

Summary by CodeRabbit

  • New Features
    • Improved type inference for link and script definitions using union-based rel and type values, with enhanced IDE support and field validation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

PR enhances InferLink<T> and InferScript<T> type resolvers to correctly handle union-valued discriminators (rel and type). New union helpers compute structural intersections of matching variants, enabling field validation across union inputs. Comprehensive test coverage validates the union narrowing behavior for both defineLink and defineScript.

Changes

Union-aware type inference for link and script

Layer / File(s) Summary
Link union type resolution
packages/unhead/src/types/schema/link.ts
Adds UnionToIntersection, IsUnion, and InferLinkUnion<R> helpers to compute structural intersections of matching Link variants when rel is a union. Updates InferLink<T>'s KnownLinkRel branch to route union rel through the new intersection path while preserving non-union behavior.
Script union type resolution
packages/unhead/src/types/schema/script.ts
Introduces parallel union utilities (UnionToIntersection, IsUnion, InferScriptUnion<U>) and modifies InferScript<T> to resolve union type discriminators through structural intersection of compatible Script variants, with updated documentation.
Union discriminator test coverage
packages/unhead/test/unit/types.test.ts
Adds imports and comprehensive compile-time tests verifying field enforcement and optional-field propagation when rel/type are runtime-determined unions, including targeted narrowing tests and extensive adversarial edge-case coverage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • unjs/unhead#735: Introduces the foundational InferLink/InferScript type resolvers and defineLink/defineScript helpers that this PR extends with union-aware resolution logic.
  • unjs/unhead#744: Adds new rel string members (sitemap, amphtml, hub, apple-touch-startup-image); this PR's union-aware narrowing ensures those members resolve correctly when rel is a union discriminator.
  • unjs/unhead#736: Wraps resolved InferLink/InferScript shapes in DeepReadonly and relaxes readonly input compatibility in the same type files as this PR's union-discriminator changes.

🐰 Unions now play nice, types align just right,
Variants intersect with structural might,
Fields stay valid when discriminators collide,
Inference flows smooth with nowhere to hide.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding support for union rel/type in defineLink and defineScript factory functions.
Description check ✅ Passed The description follows the template structure with all required sections completed: linked issue, type of change (marked appropriately), and detailed description explaining the problem and solution.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/link-rel-resource-hints-union

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Bundle Size Gzipped
Client (Minimal) 10.8 kB 4.4 kB
Server (Minimal) 10.7 kB 4.3 kB
Vue Client (Minimal) 11.8 kB 4.9 kB
Vue Server (Minimal) 11.6 kB 4.7 kB

Allows runtime-determined rels like `cond ? 'preconnect' : 'dns-prefetch'`
and script types like `cond ? 'module' : 'text/javascript'` without casts.
Distributes the match over the input union, then intersects matching
variants (minus the discriminator) so optional fields from any branch
carry over while incompatible required fields still force the stricter
shape.
@harlan-zw harlan-zw force-pushed the fix/link-rel-resource-hints-union branch from b0ea905 to 03fd84f Compare May 27, 2026 03:12
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/unhead/src/types/schema/script.ts (1)

485-488: 💤 Low value

Consider extracting shared helper types to util.ts.

UnionToIntersection and IsUnion are duplicated verbatim in both link.ts and script.ts. Moving them to ../util.ts would eliminate duplication and centralize these type utilities.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/unhead/src/types/schema/script.ts` around lines 485 - 488, Extract
the duplicated type helpers UnionToIntersection and IsUnion from script.ts and
link.ts into a single ../util.ts file, export them there, then remove the
duplicate definitions in both script.ts and link.ts and add imports (e.g.,
import { UnionToIntersection, IsUnion } from "../util") where those types are
used; ensure exported names and usages (type UnionToIntersection<...>, type
IsUnion<...>) remain identical to avoid breaking consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/unhead/src/types/schema/script.ts`:
- Around line 485-488: Extract the duplicated type helpers UnionToIntersection
and IsUnion from script.ts and link.ts into a single ../util.ts file, export
them there, then remove the duplicate definitions in both script.ts and link.ts
and add imports (e.g., import { UnionToIntersection, IsUnion } from "../util")
where those types are used; ensure exported names and usages (type
UnionToIntersection<...>, type IsUnion<...>) remain identical to avoid breaking
consumers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d535838-0fb7-451e-8f89-5b5f615b733d

📥 Commits

Reviewing files that changed from the base of the PR and between f1158f7 and 03fd84f.

📒 Files selected for processing (3)
  • packages/unhead/src/types/schema/link.ts
  • packages/unhead/src/types/schema/script.ts
  • packages/unhead/test/unit/types.test.ts

@harlan-zw harlan-zw merged commit e4663eb into main May 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant