Ввод компонента-преобразователя карты компонентов для общего использования - PullRequest
0 голосов
/ 07 ноября 2019

Я работаю над проектом, в котором во внутреннем интерфейсе можно моделировать динамический контент, который впоследствии отображается как Компоненты во внешнем интерфейсе.

Он работает по большей части, за исключением работы с моделями, в которых динамический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"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...