twin.examples icon indicating copy to clipboard operation
twin.examples copied to clipboard

Typing component props with Stitches

Open kyh opened this issue 4 years ago • 2 comments

I've set up twin macros with stitches following the examples but my styled-components export any type.

I was wondering if there's a way to type it so that I can autocomplete the props in VSCode

Screen Shot 2021-07-26 at 12 35 22 AM

Here's my twin.d.ts

import "twin.macro";
import { css as cssImport } from "@stitches/react";
import styledImport from "@stitches/react";

// Support a css prop when used with twins styled.div({}) syntax
type CSSProp<T = AnyIfEmpty<DefaultTheme>> = string | CSSObject;

declare module "react" {
  // The css prop
  interface HTMLAttributes<T> extends DOMAttributes<T> {
    css?: CSSProp;
    tw?: string;
  }
  // The inline svg css prop
  interface SVGProps<T> extends SVGProps<SVGSVGElement> {
    css?: CSSProp;
    tw?: string;
  }
}

// Support twins styled.div({}) syntax
type StyledTags = {
  [Tag in keyof JSX.IntrinsicElements]: CreateStyledComponent<
    JSX.IntrinsicElements[Tag]
  >;
};

declare module "twin.macro" {
  // The styled and css imports
  const styled: typeof StyledTags | typeof styledImport;
  const css: typeof cssImport;
}

kyh avatar Jul 26 '21 07:07 kyh

I have a workaround to the issue above by forwarding the ref in order to add props to the exported component:

interface Props {
  children?: ReactNode;
  size?: "xs" | "sm" | "md" | "lg" | "xl";
}

type Ref = ReactNode | HTMLElement | string;

const StyledButton = styled.button({
  ...
});

const Button = forwardRef<Ref, Props>(function Button({ ...rest }, ref) {
  return <StyledButton ref={ref} {...rest} />;
});

export default Button;

kyh avatar Jul 26 '21 08:07 kyh

twin.d.ts

import { css as cssImport } from '@stitches/react';
import type * as StyledComponent from '@stitches/react/types/styled-component';
import type * as Util from '@stitches/react/types/util';

import { config, CSS } from '../stitches.config';

import 'twin.macro';

type CSSProp<T = AnyIfEmpty<DefaultTheme>> = CSSObject | string;

type Stitches<Type = string> = <
	Composers extends (
		| string
		| React.ExoticComponent<any>
		| React.JSXElementConstructor<any>
		| Util.Function
		| { [name: string]: unknown }
	)[],
>(
	...composers: {
		[K in keyof Composers]: Composers[K] extends
			| string
			| React.ExoticComponent<any>
			| React.JSXElementConstructor<any>
			| Util.Function
			? Composers[K]
			: CSS & {
					variants?: {
						[Name in string]: {
							[Pair in number | string]: CSS;
						};
					};
					compoundVariants?: (('variants' extends keyof Composers[K]
						? {
								[Name in keyof Composers[K]['variants']]?:
									| Util.Widen<keyof Composers[K]['variants'][Name]>
									| Util.String;
						  } &
								Util.WideObject
						: Util.WideObject) & {
						css: CSS;
					})[];
					defaultVariants?: 'variants' extends keyof Composers[K]
						? {
								[Name in keyof Composers[K]['variants']]?:
									| Util.Widen<keyof Composers[K]['variants'][Name]>
									| Util.String;
						  }
						: Util.WideObject;
			  } & {
						[K2 in keyof Composers[K]]: K2 extends 'compoundVariants' | 'defaultVariants' | 'variants'
							? unknown
							: K2 extends keyof CSS
							? CSS[K2]
							: unknown;
					};
	}
) => StyledComponent.StyledComponent<Type, StyledComponent.StyledComponentProps<Composers>, typeof config.media, CSS>;

type StyledTags = {
	[Element in keyof JSX.IntrinsicElements]: Stitches<Element>;
};

declare module 'react' {
	interface HTMLAttributes<T> extends DOMAttributes<T> {
		css?: CSSProp;
		tw?: string;
	}

	interface SVGProps<T> extends SVGProps<T> {
		css?: CSSProp;
		tw?: string;
	}
}

declare module 'twin.macro' {
	const css: typeof cssImport;
	const styled: StyledTags;
}

stitches.config.ts

import { createStitches, CSS as StitchesCSS } from '@stitches/react';

const stitches = createStitches({});

export const { css, config, globalCss, keyframes, styled, theme, getCssText } = stitches;
export type CSS = StitchesCSS<typeof stitches>;

This is how I got this "working" in my project.

Few things to note, in the stitches config you need to make sure config and type CSS are exported.

This was tested with v1.0.0-canary.15 of @stitches/react, not v0.2.x.

matthewpi avatar Aug 22 '21 20:08 matthewpi