javascript - Bruke punktnotasjon med funksjonell komponent i TypeScript
Den offisielle ReactJs-dokumentasjonen anbefaler å lage komponenter ved å følge punktnotasjon, som React-bootstrap-biblioteket:
<Card>
<Card.Body>
<Card.Title>Card Title</Card.Title>
<Card.Text>
Some quick example text to build on the card title and make up the bulk of
the card's content.
</Card.Text>
</Card.Body>
</Card>
Takket være dette spørsmålet vet jeg at jeg kan lage denne strukturen ved å bruke funksjonelle komponenter akkurat slik i JavaScript:
const Card = ({ children }) => <>{children}</>
const Body = () => <>Body</>
Card.Body = Body
export default Card
Ved å bruke TypeScript bestemte jeg meg for å legge til de tilsvarende typene til det:
const Card: React.FunctionComponent = ({ children }): JSX.Element => <>{children}</>
const Body: React.FunctionComponent = (): JSX.Element => <>Body</>
Card.Body = Body // <- Error: Property 'Body' does not exist on type 'FunctionComponent<{}>'
export default Card
Problemet nå er at TypeScript ikke tillater tildelingen Card.Body=Body og gir meg feilen:
Egenskapen 'Body' eksisterer ikke på typen 'FunctionComponent<{}>'
Så hvordan kan jeg skrive dette riktig for å bruke denne kode-strukturen?
Svar #1
const Card: React.FunctionComponent & { Body: React.FunctionComponent } = ({ children }): JSX.Element => <>{children}</>
const Body: React.FunctionComponent = (): JSX.Element => <>Body</>
Card.Body = Body;
Eller mer lesbart:
type BodyComponent = React.FunctionComponent;
type CardComponent = React.FunctionComponent & { Body: BodyComponent };
const Card: CardComponent = ({ children }): JSX.Element => <>{children}</>;
const Body: BodyComponent = (): JSX.Element => <>Body</>;
Card.Body = Body;
Svar #2
Jeg fant en smart måte å bruke Object.assign på for å få punktnotasjon til å fungere med ts. Det var brukstilfeller som lignet på
type TableCompositionType = {
Head: TableHeadComponentType;
Body: TableBodyComponentType;
Row: TableRowComponentType;
Column: TableColumnComponentType;
};
type TableType = TableComponentType & TableCompositionType;
export const Table: TableType = TableComponent;
Table.Head = TableHeadComponent;
Table.Body = TableBodyComponent;
Table.Row = TableRowComponent;
Table.Column = TableColumnComponent;
der ts ville kaste feil. Min grunnleggende fungerende løsning var:
export const Table: TableType = Object.assign(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
Den eneste ulempen er at mens resultatet vil bli typekontrollert, vil ikke de enkelte underkomponentene inne i objektparameteren bli det, noe som kan være nyttig for feilsøking.
En god praksis ville være å definere (og typekontrollere) parameteren på forhånd.
const tableComposition: TableCompositionType = {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
};
export const Table: TableType = Object.assign(TableComponent, tableComposition);
Men siden Object.assign er generisk, er dette også gyldig:
export const Table = Object.assign<TableComponentType, TableCompositionType>(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
Selvfølgelig, hvis du ikke trenger å (eller ønsker å) spesifikt angi typen på forhånd, kan du også gjøre det, og den vil bare bli utledet. Ingen nasty hacks er nødvendig.
export const Table = Object.assign(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
