Výchozí nastavení obecných parametrů v TypeScript

TypeScript 2.3 implementoval výchozí výchozí hodnoty obecných parametrů které umožňují určit výchozí typy pro parametry typu v obecném typu.

V tomto příspěvku chci prozkoumat, jak můžeme těžit z obecných výchozích parametrů migrací následující komponenty React z JavaScriptu (a JSX) na TypeScript (a TSX):

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Nebojte se, nemusíte znát React, abyste mohli sledovat!

#Vytvoření definice typu pro třídu komponent

Začněme vytvořením definice typu pro Component třída. Každá komponenta React založená na třídě má dvě vlastnosti props a state , oba mají libovolný tvar. Definice typu by tedy mohla vypadat nějak takto:

declare namespace React {
  class Component {
    props: any;
    state: any;
  }
}

Všimněte si, že toto je značně zjednodušený příklad pro ilustrativní účely. Koneckonců, tento příspěvek není o Reactu, ale o parametrech generického typu a jejich výchozích hodnotách. Definice typu React v reálném světě na RozhodněTyped jsou mnohem více zapojeny.

Nyní dostáváme návrhy pro kontrolu typu a automatické dokončování:

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Můžeme vytvořit instanci naší komponenty takto:

<Greeting name="World" />

Vykreslení naší komponenty poskytne následující HTML, jak bychom očekávali:

<span>Hello, World!</span>

Zatím je to dobré!

#Použití obecných typů pro rekvizity a stav

Zatímco výše uvedený příklad se zkompiluje a běží v pořádku, naše Component definice typu je nepřesnější, než bychom chtěli. Protože jsme zadali props a state být typu any , kompilátor TypeScript nám moc nepomůže.

Buďme trochu konkrétnější a představíme dva obecné typy Props a State abychom mohli přesně popsat, jaký tvar má props a state vlastnosti mají:

declare namespace React {
  class Component<Props, State> {
    props: Props;
    state: State;
  }
}

Nyní vytvoříme GreetingProps typ, který definuje jedinou vlastnost s názvem name typu string a předejte jej jako argument typu pro Props zadejte parametr:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Nějaká terminologie:

  • GreetingProps je argument typu pro parametr typu Props
  • Podobně any je argument typu pro parametr typu State

Díky těmto typům nyní v rámci naší komponenty získáváme lepší kontrolu typů a návrhy automatického doplňování:

Nyní však musíme poskytněte dva typy, kdykoli rozšíříme React.Component třída. Náš počáteční příklad kódu již správně nekontroluje typ:

// Error: Generic type 'Component<Props, State>'
// requires 2 type argument(s).
class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Pokud nechceme zadat typ jako GreetingProps , můžeme opravit náš kód poskytnutím any typ (nebo jiný fiktivní typ, například {} ) pro oba Props a State zadejte parametr:

class Greeting extends React.Component<any, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Tento přístup funguje a dělá kontrolu typu šťastným, ale:Nebylo by hezké, kdyby any byly v tomto případě standardně předpokládané, takže jsme mohli jednoduše vynechat argumenty typu? Zadejte výchozí výchozí hodnoty obecných parametrů.

#Definice obecného typu s výchozím nastavením parametrů typu

Počínaje TypeScriptem 2.3 můžeme volitelně přidat výchozí typ ke každému z našich obecných parametrů typu. V našem případě nám to umožňuje zadat, že obě Props a State by měl být any zadejte, pokud není výslovně uveden argument typu:

declare namespace React {
  class Component<Props = any, State = any> {
    props: Props;
    state: State;
  }
}

Nyní náš počáteční příklad kódu kontroluje typ a znovu úspěšně kompiluje s oběma Props a State zadejte jako any :

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Samozřejmě stále můžeme explicitně poskytnout typ pro Props zadejte parametr a přepište výchozí any zadejte, stejně jako jsme to dělali dříve:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Můžeme dělat i jiné zajímavé věci. Oba parametry typu mají nyní výchozí typ, díky čemuž jsou volitelné — nemusíme je poskytovat! To nám umožňuje zadat explicitní argument typu pro Props zatímco implicitně klesá zpět na any pro State typ:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

Všimněte si, že poskytujeme pouze jeden typ argumentu. Můžeme však vynechat pouze volitelné argumenty typu zprava. To znamená, že v tomto případě není možné zadat argument typu pro State při přechodu zpět na výchozí Props typ. Podobně při definování typu nesmí za volitelnými parametry typu následovat požadované parametry typu.

#Další příklad

V mém předchozím příspěvku o třídách mixin v TypeScript 2.2 jsem původně deklaroval následující dva aliasy typu:

type Constructor<T> = new (...args: any[]) => T;
type Constructable = Constructor<{}>;

Constructable typu je čistě syntaktický cukr. Lze jej použít místo Constructor<{}> typu, abychom nemuseli pokaždé vypisovat argument generického typu. S obecnými výchozími parametry bychom se mohli zbavit dalšího Constructable zadejte dohromady a vytvořte {} výchozí typ:

type Constructor<T = {}> = new (...args: any[]) => T;

Syntaxe je o něco složitější, ale výsledný kód je čistší. Pěkné!