Я новичок в Typescript, и я пытаюсь создать компоненты React с разными вариантами, IE a Button с вариантом 'foo' и вариантом 'bar'.
Я хотел бы использовать компонент <Button variant="foo" />
, для которого я бы использовал такой интерфейс:
interface Props {
variant: 'foo' | 'bar';
prop1?: boolean;
prop2?: boolean;
}
При создании кнопки с опорой variant="foo"
я хочу разрешить одни свойства и отключить другие. То же и с другими вариантами. По этой причине я создал такие интерфейсы, которые являются результатом многих поисков в 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 />
</>
)
}
И хотя этот вид работает, в том смысле, что я не могу создать Component без правильных свойств, точка отказа, определенная Typescript, неверна.
Например, <Component variant="bar" prop1 prop2/>
throws Этому вызову не соответствует никакая перегрузка. как и ожидалось, но в нем ошибочно указано, что 'bar' is not assignable to type 'foo'
, где проблема на самом деле заключается в назначении prop1
и prop2
, которые не разрешены в BarVariant interface
.
Моя проблема, насколько я могу судить, заключается в том, что Typescript всегда, кажется, делает вывод о последней перегрузке, где я ожидал (и хотел бы!), Чтобы вывести перегрузку, связанную с опцией варианта.
Если бы кто-нибудь мог пролить свет на то, что я делаю здесь неправильно, я был бы очень признателен!