reactjs - Hur lägger man till defaultProps till en generell funktionell komponent med komponentinjektion i TypeScript?


Jag har en funktionell komponent Button som tar en prop as för att injicera ett HTML-element eller en annan komponent. Jag vill att standardvärdet för as-propen ska vara Link-komponenten från react-router. Detta skulle göra Link-ens to-prop till en obligatorisk prop för Button. Så jag borde få ett fel när jag skriver <Button>Press me!</Button>, men kompilatorn rapporterar inget fel.

Jag använder TypeScript v3.5.3 med följande inställningar i tsconfig.json:

{
  "compilerOptions": {

    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitThis": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

Här är min nuvarande situation (förenklad) med Link som standard för as es6-stil:

import React from 'react'
import { Link } from 'react-router-dom'

interface ButtonProps {
  children?: string
}

type ElementInjector<T extends React.ElementType> = {
  as: T
} & React.ComponentPropsWithoutRef<T>

export const Button = <T extends React.ElementType>({
  as: Tag = Link,
  ...props
}: ButtonProps & ElementInjector<T>) => {
  return <Tag {...props} />
}

Så med tanke på ovanstående är detta resultatet och förväntningarna:

<Button>Button</Button>            // error   - asking for `as` not `to`
<Button as={Link}>Button</Button>  // error   - ok
<Button to="/">Button</Button>     // error   - asking for `as`
<Button as="a">Button</Button>     // success - ok

Hur får jag den första att misslyckas?

Det skulle också vara coolt att lösa följande också. Inte relaterat till defaultProps utan till det generiska tillvägagångssättet.

<Button as="a" to="/">Button</Button>      // success - error expected
<Button<'a'> as="a" to="/">Button</Button> // error   - ok

Exempel att leka med

Svar #1

Parameterns standardvärde kommer inte att tas i beaktande när typparametrarna till komponenten härleds. Eftersom det inte finns någon annan plats att ta T ifrån kommer det att standardiseras till unknown. Lösningen skulle vara att lägga till en standardtyp för T

export const Button = <T extends React.ElementType = typeof Link>({
    tag: Tag = Link,
    ...props
  }: ButtonProps & ElementInjector<T>) => {
    return <Tag {...props} />
  }

let x = <Button>Button</Button>                      // error expected
let x1 = <Button tag={Link}>Button</Button>          // error   - ok
let x2 = <Button to="/">Button</Button>              // success - ok
let x3 = <Button tag="a">Button</Button>             // success - ok

Om ditt andra problem, det är liknande den här frågan men jag har inte kunnat få det att fungera, kommer att posta om jag har en uppdatering.