How toSimplify Component Imports with TypeScript Namespaces

If you're building a component that has some props, you probably have a file like this:

components/button.tsx
export type ButtonProps = { ... }; export function Button(props: ButtonProps) { ... }

This way anyone using this component can import both the component and the props type.

components/better-button.tsx
import { Button, type ButtonProps } from "./button"; export type BetterButtonProps = ButtonProps & { ... }; export function BetterButton(props: BetterButtonProps) { ... }

But notice how you need to import both the Button and ButtonProps as separate things, this also force you to name the props type as ButtonProps instead of just Props to avoid possible name conflicts (yes you could export it as Props and rename it on import, but that's just most work).

Using TypeScript namespaces, you can combine this into a single imported value.

components/button.tsx
export namespace Button { export type Props = { ... } } export function Button(props: Button.Props) { ... }

Now when import this component on our BetterButton component, we can do it this way:

components/better-button.tsx
import { Button } from "./button"; export namespace BetterButton { export type Props = Button.Props & { ... }; } export function BetterButton(props: BetterButton.Props) { ... }

Note how we only imported Button but got both the component and the namespace, and inside the namespace we could have more types.

components/button.tsx
export namespace Button { export type Variant = "solid" | "ghost" | "outline"; export type Size = "xs" | "sm" | "md" | "lg" | "xl"; export type Props = { variant: Variant; size: Size; } } export function Button(props: Button.Props) { ... }

Now if we want to use them we could import Button and use Button.Variant or Button.Size.