Skip to content

Conversation

jp-knj
Copy link
Contributor

@jp-knj jp-knj commented Aug 23, 2025

Closes #927

Background

Polymorphic components are a common pattern in modern web development where a component can render as different HTML elements. The as prop is the standard convention for this pattern:

---
interface Props {
  as?: string;
  href?: string;
}

const { as: Component = 'div', href } = Astro.props;
---

<Component href={href}>
  <slot />
</Component>

This pattern allows users to create flexible components that can adapt their rendered element based on usage context.

Problem

When using as as a prop name, the Astro compiler fails to recognize that the Props interface is being used, resulting in:

  1. TypeScript Error: 'Props' is declared but never used
  2. Type Safety Loss: The generated TSX uses Record<string, any> instead of the actual Props type
  3. Developer Experience: False positive errors that require awkward workarounds

Root Cause

The issue stems from the compiler's Props detection logic (GetPropsType in js_scanner.go). When processing the code, it encounters the as keyword and incorrectly assumes it's part of an import alias pattern (import { Props as Something }), causing it to reset the Props detection.

The compiler didn't distinguish between:

  • Import aliasing context: import { Props as Something } → should reset Props
  • Object destructuring context: const { as: Component } → should NOT reset Props

This bug was introduced as a side effect of fixing #814, which added support for Props import aliasing but was too aggressive in its pattern matching.

Changes

  • Fixes Props interface being incorrectly marked as "unused" when using as as a prop name
  • Adds context checking to distinguish between:
    • Import aliasing: import { Props as Something }
    • Property destructuring: const { as: Component } = Astro.props

Before:

// TSX output when using 'as' prop:
export default function Component(_props: Record<string, any>): any {}
// ❌ TypeScript error: 'Props' is declared but never used

After:

// TSX output when using 'as' prop:
export default function Component(_props: Props): any {}
// ✅ Props correctly recognized

Testing

  • Added comprehensive test cases for Props detection with 'as' prop name
  • Tests cover both with and without type assertions
  • Manually verified TSX output generates correct Props type

Docs

Bug fix only - no documentation changes needed. The compiler now correctly handles a previously broken edge case.

Fixed Props interface being marked as unused when using 'as' as a prop name. The compiler now distinguishes between import aliasing and property destructuring contexts.

- Add context checking to prevent false positives
- Add test coverage for 'as' prop scenarios
Copy link

changeset-bot bot commented Aug 24, 2025

🦋 Changeset detected

Latest commit: d69726a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@astrojs/compiler Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jp-knj jp-knj marked this pull request as ready for review August 24, 2025 01:18
@ematipico
Copy link
Member

Thank you @jp-knj

Can you add some tests? https://github.com/withastro/compiler/blob/main/packages/compiler/test/tsx/props.ts

@jp-knj
Copy link
Contributor Author

jp-knj commented Sep 1, 2025

Thanks, @ematipico
updated, I checked if the compiler can correctly tell the difference between import aliases and taking out properties. it's sure that TypeScript keeps type safety.

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.

🐛 BUG: using as a prop name cause Props to not be recognized in TSX
2 participants