Аз съм начинаещ в Typescript и се опитвам да създам компоненти на React с различни варианти, т.е. бутон с вариант „foo“ и „bar“.
Бих искал да използвам компонента <Button variant="foo" />
, за който бих използвал такъв интерфейс:
interface Props {
variant: 'foo' | 'bar';
prop1?: boolean;
prop2?: boolean;
}
Когато създавам бутон с реквизит variant="foo"
, искам да разреша определени реквизити и да деактивирам други. Същото и с другите варианти. Поради тази причина създадох интерфейси като so, което е резултат от много търсения в google / stackoverflow:
interface Props {
variant?: never;
prop1?: boolean;
prop2?: boolean;
}
type Variant = 'foo' | 'bar' | 'baz';
interface DefaultVariant extends Omit<Props, 'variant'> {
variant: Variant;
}
interface FooVariant extends DefaultVariant {
variant: 'foo';
prop1?: never;
}
interface BarVariant extends DefaultVariant {
variant: 'bar';
prop1?: never;
prop2?: never;
}
interface BazVariant extends DefaultVariant {
variant: 'baz';
prop1?: never;
prop3: boolean;
}
... за да се използват претоварвания на функции като така:
export default function Component(props: Props): ReactElement;
export default function Component(props: BazVariant): ReactElement;
export default function Component(props: BarVariant): ReactElement;
export default function Component(props: FooVariant): ReactElement;
export default function Component(props: Props | BarVariant | BazVariant | FooVariant): ReactElement {
const ComponentVariant = variants[props.variant || 'foo']
return (
<ComponentVariant />
)
}
const Foo = () => <div>Foo</div>
const Bar = () => <div>Bar</div>
const Baz = () => <div>Baz</div>
type Variants = {[key in Variant]: (props: any) => ReactElement };
const variants: Variants = {
foo: Foo,
bar: Bar,
baz: Baz,
}
Надявайки се да използвам компонента така:
function App(): ReactElement {
return (
<>
{/* should work: */}
<Component />
<Component prop1 prop2/>
<Component variant="foo" prop2 />
<Component variant="bar" />
<Component variant="baz" prop2 prop3 />
{/* should fail on prop1: */}
{/* this works */}
<Component variant="foo" prop1 prop2 />
{/* should fail on presence of prop1 and prop2: */}
{/* but fails on variant and prop1: */}
{/* No overload matches this call.
The last overload gave the following error.
Type '"bar"' is not assignable to type '"foo"'.
Type 'true' is not assignable to type 'undefined' */}
<Component variant="bar" prop1 prop2 />
{/* should fail on presence of prop1 and lack of prop3: */}
{/* but fails on variant: */}
<Component variant="baz" prop1 prop2 />
</>
)
}
И докато този вид работи, в смисъл, че не мога да създам компонент без правилните подпори, точката на повреда, определена от Typescript, не е правилна.
Например <Component variant="bar" prop1 prop2/>
хвърля Никакво претоварване не съответства на това извикване. както се очаква, но погрешно се посочва, че 'bar' is not assignable to type 'foo'
, където проблемът всъщност е в присвояването на prop1
и prop2
, които не са разрешени в BarVariant interface
.
Моят проблем, доколкото мога да преценя, се крие във факта, че Typescript изглежда винаги извежда последното претоварване, където очаквах (и бих искал!) да изведе претоварването, свързано с опцията за вариант.
Ако някой може да сподели малко светлина относно това, което правя грешно тук, ще бъда много оценен!