Я работаю над проектом, в котором во внутреннем интерфейсе можно моделировать динамический контент, который впоследствии отображается как Компоненты во внешнем интерфейсе.
Он работает по большей части, за исключением работы с моделями, в которых динамическийcontent - это массив типов объединения, который затем должен быть преобразован в компоненты.
Это просто сделать в простом JavaScript, так как никаких проблем нет, но я ищу статический тип сопоставления компонентов.
Моя цель - создать средство отображения, которое будет работать с этим синтаксисом и выводить имена типов (из ключей) и реквизиты (из значений), предотвращая значения, отличные от ComponentType
:
// PageComponentMapper.ts
import componentMapperFactory from './componentMapperFactory'
import Hero from './Hero'
import InstagramFeed from './InstagramFeed'
import FrameBuilder from './FrameBuilder'
export default componentMapperFactory({
Hero,
InstagramFeed,
FrameBuilder
})
При использовании:
import { ComponentProps } from 'react'
import PageComponentMapper from './PageComponentMapper'
type PageComponentMapperProps = ComponentProps<typeof PageComponentMapper>
type PageContentItem =
PageComponentMapperProps['props'] &
{ key: string } &
{ type: PageComponentMapperProps['type'] }
Производство:
type PageComponentMapperProps = {
props: HeroProps | InstagramFeedProps | FrameBuilderProps,
type: "Hero" | "InstagramFeed" | "FrameBuilder"
}
Как далеко у меня есть: https://codesandbox.io/s/relaxed-star-6tqzz
С помощью этой реализации сопоставления компонентов:
import { createElement, ComponentType } from "react";
interface ComponentResolverProps<
ResolvableComponentTypeNames,
ResolvableComponentPropTypes extends {}
> {
type: ResolvableComponentTypeNames;
props: ResolvableComponentPropTypes;
}
// How to infer type names and prop types from map?
export default function componentMapperFactory<
AvailableComponentTypeNames extends string,
AvailableComponentPropTypes
>(
map: {
[K in AvailableComponentTypeNames]: ComponentType<
AvailableComponentPropTypes
>
}
) {
return function resolveComponentFromMap({
type,
props
}: ComponentResolverProps<
AvailableComponentTypeNames,
AvailableComponentPropTypes
>) {
const component = map[type];
if (!component) {
return null;
}
return createElement(component, props);
};
}
Ранее я фактически не предоставлял имена типов и по умолчанию использовал строку, что мне не помоглоразличать имена.
Я также взломал:
export default function componentMapperFactory<
C extends { [key: string]: ComponentType }
>(
map: {
[N in keyof C]: C[N] extends ComponentType<infer P>
? (P extends unknown ? ComponentType<{}> : C[N])
: never
}
) {
return function resolveComponentFromMap({
type,
props
}: {
type: keyof C;
props: ComponentProps<C["string"]>;
}) {
const component = map[type];
if (!component) {
return null;
}
return createElement(component, props);
};
}
Но это теряет типы реквизитов, я понятия не имею, почему, а также это кажется сумасшедшим сложным синтаксисом мудрым. https://codesandbox.io/s/unruffled-hill-i5cbr
Итак, чтобы подвести итог: как мне преобразовать componentMapperFactory
, чтобы я мог использовать его без явных аргументов типа:
export default componentMapperFactory({
Hero,
InstagramFeed,
FrameBuilder
})
И получить этивведите как результат для ComponentProps<typeof PageComponentMapper>
?
type PageComponentMapperProps = {
props: HeroProps | InstagramFeedProps | FrameBuilderProps,
type: "Hero" | "InstagramFeed" | "FrameBuilder"
}