reactjs - İç içe geçmiş nesne özellikleri için Typescript jenerik tipi


Bunun gibi bir şeyi başarmak istiyorum, böylece Component türü çıkarılabilir ve props otomatik olarak güvenli bir şekilde türlenebilir.

type ComponentsType<T extends React.FC<any>> {
  [key: string]: {
    Component: T;
    props?: React.ComponentProps<T>
  };
}

const components: ComponentsType = {
   RedButton: {
      Component: Button,
      props: { // <- this should be automatically typed to take props of Button
        color: 'red'
      }
   },
   BlueButton: {
      Component: Button,
      props: { // <- this should be automatically typed to take props of Button
         color: 'blue'
      }
   },
   FullWidthCard: {
      Component: Card,
      props: { // <- this should be automatically typed to take props of Card
         variant: 'full-width'
      }
   },
}

Ama yukarıdaki yazım çalışmayacak çünkü generic kısmı nesnenin kökünde yani components'te, her bir özellikte yani RedButton veya FullWidthCard'da değil.

Peki, TypeScript'in bunların üzerinden teker teker geçip Component'i teker teker türetebileceği bir yol var mı?

Teşekkürler

Cevap #1

Beklenen davranışı elde etmek için, her bileşeni ve özellikleri fonksiyon argümanlarından çıkarmanız gerekir. Yani, ekstra bir fonksiyon oluşturmanız gerekir.

import React, { FC, ComponentProps } from 'react'


const Button: FC<{ color: string }> = (props) => null
const Card: FC<{ variant: string }> = (props) => null


type WithProps<T> =
    T extends Record<infer Key, infer _> ? {
        [P in Key]: T[Key] extends { Component: infer Comp, props: infer PassedProps }
        ? Comp extends React.JSXElementConstructor<infer Prps>
        ? { Component: Comp, props: ComponentProps<Comp> & Record<Exclude<keyof PassedProps, keyof Prps>, never> }
        : never
        : never
    } : never


const components = <
    Comp extends React.JSXElementConstructor<any>,
    Item extends { Component: Comp },
    Components extends Record<string, Item>>(dictionary: Components & WithProps<Components>) => {}

Oyun alanı

Comp - türetilmiş bileşen

Öğe - çıkarılan sözlük öğesi, bileşen&özellikler nesnesi

Bileşenler - çıkarılan tüm argüman.

WithProps - geçirilen argümandaki her nesneyi döngüye sokar ve props nesnesinin bileşen propslarına atanabilir olup olmadığını kontrol eder. Eğer evet ise - props döndürür, aksi takdirde tüm geçersiz propsları never olarak reddeder.

components({
    RedButton: {
        Component: Button,
        props: {
            color: 'red'
        }
    },
    BlueButton: {
        Component: Button,
        props: {
            extra_prop:42, // DRAWBACK, no error
            color: 'blue'
        }
    },
    FullWidthCard: {
        Component: Card,
        props: {
            variant_invalid_prop: 'full-width' // expected error
        }
    },
})

Ama bir dezavantaj var, bu fonksiyon ekstra özellikleri kabul edecek, örneğin extra_prop.

Not: Umarım biri, ekstra özelliklerin de geçersiz olacağı daha iyi bir çözüm bulur.