有多个泛型的条件式React组件属性



我正在尝试在React中实现Typography组件,并且需要使其足够灵活,因此它将:

  1. variantprop是强制性的

  2. as道具是可选的

    。如果as是一个字符串,它将是一个本地html标签,并显示该标签的属性

    b。如果as是一个react元素,它将显示react元素的属性

    c。如果没有定义as,它将默认为变体的映射元素

例如,如果as="a",我们期望href属性存在。

示例代码如下,当测试类型Proxy时,它只工作,但与react组件一起使用时,它不起作用:

import React from "react";
const TypographyDefaultMapping = {
h1: 'h1',
h2: 'h2',
h3: 'h3',
'body-medium': 'span',
label: 'span',
} as const;
export type TypographyVariants = keyof typeof TypographyDefaultMapping;
type Tags = keyof HTMLElementTagNameMap;
type AsType = undefined | Tags | React.FunctionComponent<any>;
type VariantsDefaultType<T extends TypographyVariants> = Omit<
React.ComponentPropsWithoutRef<typeof TypographyDefaultMapping[T]>,
'as'
> & {
variant: TypographyVariants;
};
export type NewConditionalTypography<
T extends AsType,
V extends TypographyVariants
> = T extends undefined
? VariantsDefaultType<V> // Undefined "as" property, fallback to variant's default
: T extends Tags
? {
variant: TypographyVariants;
as: Tags;
bloup: boolean;
} & React.ComponentPropsWithoutRef<T> // "as" property is a valid HTML tag
: T extends React.FunctionComponent<any> // "as" property is a React element
? {
variant: TypographyVariants;
as: React.FunctionComponent<any>;
bib: boolean;
} & React.ComponentProps<T>
: never;
type Proxy<T = unknown> = T extends {
as: infer As extends AsType;
variant: infer Var extends TypographyVariants;
}
? NewConditionalTypography<As, Var>
: T extends {
variant: infer Var extends TypographyVariants; // case of not defined "as"
}
? NewConditionalTypography<undefined, Var> 
: never;
const NewTypography = (props: Proxy) => (
<div>Test: {JSON.stringify(props)}</div>
);
const Link = ({ linkClick }: { linkClick: () => void }) => (
<button onClick={linkClick}>Test</button>
);
// Expected to get HTMLHeadingElement props + variant prop
type TestProxy = Proxy<{ variant: 'h2' }>;
// ^?
// Expected to get HTMLSpanElement props + variant prop
type TestProxySpan = Proxy<{ variant: 'body-medium' }>;
// ^?
// Expected to get HTMLAnchor props + variant prop
type TestProxy2 = Proxy<{ variant: 'h2'; as: 'a' }>;
// ^?
// Expected to get Link element props + variant prop
type TestProxy3 = Proxy<{ variant: 'h2'; as: typeof Link }>;
// ^?
const TestingComponent = () => (
<div>
// Should allow (and auto-complete) "href"
<NewTypography variant="h2" />
// Should allow (and auto-complete) "linkClick"
<NewTypography
variant="body-medium"
as={Link}
//   linkClick={() => console.log('Test')}
/>
// Should not allow "href"
<NewTypography variant="body-medium" as="span" href="www.example.com" />
</div>
);

或Typescript Playground链接。

有什么线索可以实现这一点,如果它是可行的吗?

[编辑]:

  1. 基于注释的简化游乐场

您需要将类型参数添加到函数中,以便可以推断它们。之后,对NewConditionalTypography进行一些小调整,它应该可以工作了:


export type NewConditionalTypography<
T extends AsType,
V extends TypographyVariants
> = // Undefined "as" property, fallback to variant's default
T extends undefined? VariantsDefaultType<V>: 
// "as" property is a React element
T extends (p: any) => JSX.Element ? { bib: boolean; } & React.ComponentProps<T>: 
// "as" property is a valid HTML tag
T extends Tags ? { bloup: boolean; } & React.ComponentPropsWithoutRef<T> :
{}
const NewTypography = <TVar extends TypographyVariants, TAs extends AsType = undefined>(props: {
as?: TAs;
variant: TVar;
} & NewConditionalTypography<TAs, TVar> ) => (
<div>Test: {JSON.stringify(props)}</div>
);

操场上联系

相关内容

  • 没有找到相关文章