Overview
TypeScript 2.3 implemented generic parameter defaults which allow you to specify default types for type parameters in a generic type.
In this post, I want to explore how we can benefit from generic parameter defaults by migrating the following React component from JavaScript (and JSX) to TypeScript (and TSX):
|
|
Don’t worry, you don’t have to know React to follow along!
Creating a Type Definition for the Component Class
Let’s start by creating a type definition for the Component class. Each class-based React component has the two properties props and state, both of which have arbitrary shape. A type definition could therefore look something like this:
|
|
Note that this is a vastly { 非常,极大地 } oversimplified { 过于简单化的 } example for illustrative { 作例证用的,解释性的 } purposes. After all, this post is not about React, but about generic type parameters and their defaults. The real-world React type definitions on DefinitelyTyped are a lot more involved { 复杂的 }.
Now, we get type checking and autocompletion suggestions:
|
|
We can create an instance of our component like this:
|
|
Rendering our component yields the following HTML, as we would expect:
|
|
So far, so good!
Using Generic Types for Props and State
While the above example compiles and runs just fine, our Component type definition is more imprecise { 不精确的 } than we’d like. Since we’ve typed props and state to be of type any, the TypeScript compiler can’t help us out much.
Let’s be a little more specific and introduce two generic types Props and State so that we can describe exactly what shape the props and state properties have:
|
|
Let’s now create a GreetingProps type that defines a single property called name of type string and pass it as a type argument for the Props type parameter:
|
|
Some terminology { 术语 }:
GreetingPropsis the type argument for the type parameterProps- Similarly,
anyis the type argument for the type parameterState
With these types in place, we now get better type checking and autocompletion suggestions within our component:
However, we now must provide two types whenever we extend the React.Component class. Our initial code example no longer type-checks correctly:
|
|
If we don’t want to specify a type like GreetingProps, we can fix our code by providing the any type (or another dummy type such as {}) for both the Props and State type parameter:
|
|
This approach works and makes the type checker happy, but: Wouldn’t it be nice if any were assumed by default in this case so that we could simply leave out { 省略 } the type arguments? Enter generic parameter defaults.
Generic Type Definitions with Type Parameter Defaults
Starting with TypeScript 2.3, we can optionally add a default type to each of our generic type parameters. In our case, this allows us to specify that both Props and State should be the any type if no type argument is given explicitly:
|
|
Now, our initial code example type-checks and compiles successfully again with both Props and State typed as any:
|
|
Of course, we can still explicitly provide a type for the Props type parameter and override the default any type, just as we did before:
|
|
We can do other interesting things as well. Both type parameters now have a default type, which makes them optional — we don’t have to provide them! This allows us to specify an explicit type argument for Props while implicitly falling back to any for the State type:
|
|
Note that we’re only providing a single type argument. We can only leave out optional type arguments from the right, though. That is, it’s not possible in this case to specify a type argument for State while falling back to the default Props type. Similarly, when defining a type, optional type parameters must not be followed by required type parameters.
Another Example
In my previous post about mixin classes in TypeScript 2.2, I originally declared the following two type aliases:
|
|
The Constructable type is purely syntactic { 句法的;语法的 } sugar. It can be used instead of the Constructor<{}> type so that we don’t have to write out the generic type argument each time. With generic parameter defaults, we could get rid of the additional Constructable type altogether { 完全地;总共;总之 } and make {} the default type:
|
|
The syntax is slightly more involved, but the resulting code is cleaner. Nice!