如何从多种类型的列表项生成通用列表组件



我使用Next.js、typescript和typegraphql制作了一些Table组件
有各种自定义对象类型的数据,但最终应该将它们呈现为表行。

尽管由于每个数据的形状不同,行的详细形状也会有所不同,但
它们的相同之处在于,它们应该呈现为行列表。

所以我想通过列表数据的类型来制作通用列表组件,以减少重复代码的编写。

我将用动物的数据类型来大致解释代码
当前在CatList.tsx:内部

const CatList = () => {
const { list, total } = useQuery(/* data fetching */)
// list is type of Cat[]

return (
<BaseTable>
{/* this block is what I wanted to make into generic components */}
{list.map((item: Cat) => (
<CatListRow data={item} /*...other props*/ />
)}
</BaseTable>
)
}
// same with dog in different file
const DogList = () => {
const { list, total } = useQuery(/* data fetching */)
// list is type of Dog[]

return (
<BaseTable>
{list.map((item: Dog) => (
<DogListRow data={item} /*...other props*/ />
)}
</BaseTable>
)
}

我想把每个清单都列成这样:

const CatList = () => {
const { list, total } = useQuery(/* data fetching */)

return (
<BaseTable>
<Rows list={list} /* ...other props */ />
</BaseTable>
)
}

我试着把Rows.tsx做成这样:

type TableTarget = Cat | Dog | ... ;
/** each type has common fields
* __typename (string - ex: "Cat")
* id
* createdAt
* ...etc
*/
interface RowProps<T> {
data: T;
}
const CatRow = ({ data }: RowProps<Cat>) => {
/* Cat Row */
}
const DogRow = ({ data }: RowProps<Dog>) => {
/* Dog Row */
}

const getRowComponentByType = (target: TableTarget) => {
switch(target.__typename) {
case 'Cat':
return CatRow;
case 'Dog':
return DogRow;
...
}
}

interface RowsProps<T extends TableTarget> {
list: T[];
}
const Rows = <T>({ list }: RowsProps<T>) => {
if (list.length === 0) {
return (
<tr>list is empty.</tr>
)
}

const Row = getRowComponentByType(list[0])
return (
<>
{list.map((item: T) => {
<Row data={item} />
{/* red underline on 'data' prop */}
{/* error message: 'TableTarget' is not assignable to 'Cat & Dog' */}
})}
</>
)
}
export default Rows;

我想知道如何在正确使用typescript泛型的情况下完成Rows组件。

提前谢谢。

这对我有用,但如果你需要进一步的帮助,我想我需要一个stackblitz

type Cat = {};
type Dog = {};
type TableTarget = Cat | Dog;
interface RowsProps<T extends TableTarget> {
list: T[];
}
function Rows<T extends TableTarget>({ list }: RowsProps<T>): any {
list.map(() => {
})
}
function row<T extends TableTarget>(props: RowsProps<T>): any{
return {list: []}
}

最新更新