Добавление дополнительного свойства интерфейса к компоненту, если он используется как дочерний

#typescript #jsx #typescript-generics #tsc

#typescript #jsx #typescript-generics #tsc

Вопрос:

Я использую TypeScript в синтаксисе JSX без использования React (или любой другой подобной библиотеки). Для jsxFactory я использую свою собственную фабрику.

Это самая простая версия того, что я хочу:

 <Panel>
  <Banner text="FREE OFFER" align="left" />
</Panel>
  

align Свойство действует required только тогда, когда Banner оно помещено внутрь Panel .

Banner может использоваться вне a Panel , но в этом случае использование align свойства должно выдавать ошибку.

Я хочу, чтобы это произошло во время компиляции.

Я уже пробовал это на игровой площадке typescriptlang.

 declare global {
    namespace JSX {
        interface ElementChildrenAttribute { children: {}; }
    }
}

interface BannerInterface {
    title: string,
    text: string
}

class Banner<T extends BannerInterface = BannerInterface>  {
    constructor(props: T) {
        //
    }
}

interface AlignmentInterface extends BannerInterface {
    align: string
}
interface PanelInterface {
    adId: string,
    children: Banner<AlignmentInterface> // I'm trying to pass the AlignmentInterface to the Banner generic
}
class Panel {
    constructor(props: PanelInterface) {
        //
    }
}

// I want this to throw compile error, as align is not in the BannerInterface
const adBanner = <Banner title="Independent Banner" text="test" align="left" />;

// I also want this to throw as Banner is being used as a children of Panel,
// and for that I want the align prop to be required.
const panelBanner = (<Panel adId="1">
                        <Banner title="Banner inside Panel" text="test" />
                     </Panel>);


export default { Panel, Banner };
  

Комментарии:

1. Я бы предложил тип более высокого порядка, переключите его, что-то вроде children: Aligned<Banner> . Немного странно говорить Banner , что он может принимать только align тогда, когда он находится внутри Panel на уровне типа, потому что что, если вы назначите const MyBanner = () => <Banner ... /> , а затем отобразите это на панели?

2. Привет @jonrsharpe, спасибо за ваш ответ. Я понял вашу точку зрения. Для моего варианта использования у меня также есть проверка во время выполнения. Итак, если a Banner динамически добавляется внутри a Panel , я создаю исключение во время выполнения, что достаточно хорошо. Но я хочу, чтобы проверка JSX во время компиляции (и предложения по редактированию кода VS) была введена как вложенный шаблон. Кроме того, если я не использую JSX, а просто использую new Panel(...children: new Banner()) — в этом случае я также хочу, чтобы проверка TS дочерних элементов была align в Banner. Спасибо

3. @jonrsharpe, я думаю, что я просто решил это, передав общий синтаксис из JSX, например <Panel< AlignmentInterface> title="" text="" align=""> . Это выглядит немного странно, но работает! Спасибо за ваши подсказки. 🙂