Skip to content

Commit b15e38c

Browse files
committed
fix(react): type polymorphic as prop (#1414)
1 parent b887731 commit b15e38c

5 files changed

Lines changed: 50 additions & 3 deletions

File tree

.changeset/polymorphic-as-types.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linaria/react": patch
3+
---
4+
5+
Add typing for intrinsic props selected through the polymorphic `as` prop on styled components.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as React from 'react';
2+
3+
import { styled } from '..';
4+
5+
const Button = styled.button``;
6+
7+
<Button as="a" href="/" />;
8+
// @ts-expect-error href requires an anchor-like target
9+
<Button href="/" />;

packages/react/__dtslint__/styled.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ const StyledDiv = styled.div``;
2727
// $ExpectType "extends"
2828
isExtends<typeof StyledDiv, React.FC<React.DetailedHTMLProps<any, any>>>();
2929

30+
const StyledButton = styled.button``;
31+
StyledButton({ as: 'a', href: '/' });
32+
StyledButton.defaultProps = { as: 'a' };
33+
// @ts-expect-error href requires an anchor-like target
34+
StyledButton({ href: '/' });
35+
3036
const A = (): React.ReactElement => React.createElement('div', null);
3137
// @ts-expect-error
3238
styled(A)``;

packages/react/__dtslint__/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"compilerOptions": {
33
"module": "commonjs",
4+
"jsx": "react",
45
"lib": ["es6"],
56
"target": "ES2015",
67
"esModuleInterop": true,

packages/react/src/styled.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,36 @@ function styled(tag: any): any {
260260
};
261261
}
262262

263+
type FunctionComponentStatic<
264+
TProps,
265+
TKey extends PropertyKey,
266+
> = TKey extends keyof React.FunctionComponent<TProps>
267+
? React.FunctionComponent<TProps>[TKey]
268+
: never;
269+
270+
type StyledComponentWithAs<TProps> = {
271+
<TAs extends keyof IntrinsicElements>(
272+
props: TProps & { as: TAs } & IntrinsicElements[TAs],
273+
context?: any
274+
): ReturnType<React.FunctionComponent>;
275+
(
276+
props: TProps & { as?: React.ElementType },
277+
context?: any
278+
): ReturnType<React.FunctionComponent<TProps>>;
279+
contextTypes?: FunctionComponentStatic<TProps, 'contextTypes'>;
280+
defaultProps?: FunctionComponentStatic<
281+
TProps & { as?: React.ElementType },
282+
'defaultProps'
283+
>;
284+
displayName?: FunctionComponentStatic<TProps, 'displayName'>;
285+
propTypes?: FunctionComponentStatic<
286+
TProps & { as?: React.ElementType },
287+
'propTypes'
288+
>;
289+
};
290+
263291
export type StyledComponent<T> = WYWEvalMeta &
264-
([T] extends [React.FunctionComponent<any>]
265-
? T
266-
: React.FunctionComponent<T & { as?: React.ElementType }>);
292+
([T] extends [React.FunctionComponent<any>] ? T : StyledComponentWithAs<T>);
267293

268294
type StaticPlaceholder = string | number | CSSProperties | WYWEvalMeta;
269295

0 commit comments

Comments
 (0)