这里是我们的组件,没有类型:
const operations = {
'+': (left, right) => left + right,
'-': (left, right) => left - right,
'*': (left, right) => left * right,
'/': (left, right) => left / right,
}
function Calculator({left, operator, right}) {
const result = operations[operator](left, right)
return (
{left} {operator} {right} = {result}
)
}
const examples = (
<>
)
就在这里,你可能会注意到我们做事的方式有点不同。也许你更喜欢这样:
const Calculator = ({left, operator, right}) => (
{left} {operator} {right} ={' '}
{operations[operator](left, right)}
)
我不喜欢那里的隐式返回。这意味着你不能合理地声明变量或使用钩子。所以,即使是简单的组件,我也不会采用这种方法。
好吧,那么也许你会这样做:
const Calculator = ({left, operator, right}) => {
const result = operations[operator](left, right)
return (
{left} {operator} {right} = {result}
)
}
说实话,大多数时候这样做是可以的。我个人喜欢函数声明的吊装特性,而不是像这样的函数表达式(了解更多)。
好了,让我们在此添加一些类型。对于函数,你需要考虑输入的类型和输出的类型。让我们从输入开始:道具。首先,让我们用一个简单的类型来表示道具(我们以后会改进它):
type CalculatorProps = {
left: number
operator: string
right: number
}
有了这个,让我们尝试一些选项,把这个类型应用到我们React组件中的props对象。
为React组件打字的一个常见方法是使用内置在@types/react 中的一个泛型(我的意思是,它是内置的,对吗?所以有什么可错的?)有趣的是,你不能以这种方式输入函数声明,所以我们必须使用函数表达式:
const Calculator: React.FC = ({left, right, operator}) => {
// implementation clipped for brevity
}
这样做很好,但有三个主要问题:
- 我们的
Calculator函数现在接受了一个children道具,尽管我们没有对它做任何事情🙃(所以,这样编译:What?)。 - 你不能使用泛型。这不是一个超级常见的问题,但绝对是一个弊端。
- 我们必须使用一个函数表达式,不能使用函数声明。
好吧,也许#3不是一个主要问题,但#1是相当重要的。如果你想深入研究的话,在这个优秀的GitHub问题上还有一些其他的小问题(也可以查看React TypeScript Cheatsheet)。简单地说,不要使用React.FC (或其更长的别名React.FunctionComponent )。
我喜欢React组件的一个原因是,它们并不那么特别。下面是React组件的定义。
一个React组件是一个返回React可以渲染的东西的函数。
现在,根据@types/react ,我们只限于null 和JSX.Elements,但实际上React也可以渲染字符串、数字和布尔。在任何情况下,因为React组件只是一个返回React可以渲染的东西的函数,输入它可以像输入函数一样直接。你不需要因为它是React而做任何特别的事情。
因此,这里是我为这个组件输入道具的方式:
function Calculator({left, operator, right}: CalculatorProps) {
// implementation clipped for brevity
}
这没有React.FC 的任何缺点,也没有比输入普通函数的参数更复杂。
好吧,那么返回值呢?好吧,我们可以把它打成React.ReactElement ,或者甚至打成更宽的JSX.Element 。但说实话,我支持我的朋友Nick McCurdy的观点,他说很容易犯错误,导致返回类型太宽。因此,即使在react语境之外,我也默认不指定返回类型(依靠推理),除非必要。这里就是这样的情况。
好了,接下来的内容与React组件的类型没有太大关系,但我想你还是会觉得很有趣,所以如果你不喜欢就跳过吧。让我们改进一下CalculatorProps 的类型。作为提醒,这是我们目前的情况:
// I took the liberty of typing each of these functions as well:
const operations = {
'+': (left: number, right: number): number => left + right,
'-': (left: number, right: number): number => left - right,
'*': (left: number, right: number): number => left * right,
'/': (left: number, right: number): number => left / right,
}
type CalculatorProps = {
left: number
operator: string
right: number
}
function Calculator({left, operator, right}: CalculatorProps) {
const result = operations[operator](left, right)
return (
{left} {operator} {right} = {result}
)
}
我认为left 和right 类型很好。我不满意的是operator 。使用string 是太宽了。有一些特定的操作是允许的。例如,如果我们尝试,会发生什么:
const element =
这就是我们所说的运行时异常,我的朋友。也就是说......除非你打开了strict 模式,在这种情况下,你会在operations[operator] 上出现一个编译错误。在严格模式下,TypeScript会正确地知道,从operations 对象中访问任何字符串都不一定会返回一个可调用的函数。
有很多方法来解决这个问题。基本上,我们想把operator ,只限于支持的运算符。我们可以用一个简单的联合类型来做到这一点:
type CalculatorProps = {
left: number
operator: '+' | '-' | '*' | '/'
right: number
}
但是如果我们决定添加指数运算符(**),那么我们不仅要更新operations 对象,还要更新operator 类型,这就很烦人了。也许我们有办法在operations 对象的基础上推导出operator 的类型?为什么,有的!?
type CalculatorProps = {
left: number
operator: keyof typeof operations
right: number
}
typeof operations 是要让我们得到一个描述 对象的类型,这大致上等于。operations
type operations = {
'+': (left: number, right: number) => number
'-': (left: number, right: number) => number
'*': (left: number, right: number) => number
'/': (left: number, right: number) => number
}
keyof 部分将采取该类型的所有键,结果是'+' | '-' | '*' | '/' 🎉。
这是完成的版本(我也打了操作函数)。
const operations = {
'+': (left: number, right: number): number => left + right,
'-': (left: number, right: number): number => left - right,
'*': (left: number, right: number): number => left * right,
'/': (left: number, right: number): number => left / right,
}
type CalculatorProps = {
left: number
operator: keyof typeof operations
right: number
}
function Calculator({left, operator, right}: CalculatorProps) {
const result = operations[operator](left, right)
return (
{left} {operator} {right} = {result}
)
}
const examples = (
<>
)
我希望这能让你了解到给你的React组件打字的一个好方法。祝你好运,保重