如何在React组件中使用TypeScript
在这篇文章中,我将讨论为什么以及如何使用TypeScript为React组件打字。
你会发现如何注解组件的道具,标记道具的可选性,以及指示返回类型。
目录
1.为什么要给React组件打字?
如果你正在编写中型和大型网络应用程序,TypeScript很有用。注释变量、对象和函数可以在你的应用程序的不同部分之间创建契约。
例如,假设我是一个在屏幕上显示格式化日期的组件的作者。
tsx
interface FormatDateProps {
date: Date
}
function FormatDate({ date }: FormatDateProps): JSX.Element {
return <div>{date.toLocaleString()}</div>;
}
根据FormatDateProps 接口,组件FormatDate 的值date 的道具只能是Date 的一个实例。这就是一个约束。
为什么这个约束很重要?因为FormatDate 组件在日期实例上调用方法date.toLocaleString() ,而date prop必须是一个日期实例。否则,这个组件就不能工作。
那么,FormatDate 组件的用户就必须满足这个约束条件,并且只向date prop提供Date 实例。
tsx
<FormatDate
date={new Date()}
/>
如果用户忘记了这个约束,比如说给date prop提供一个字符串"Sep 28 2021" 。
tsx
<FormatDate
date="Sep 28 2021"
Type 'string' is not assignable to type 'Date'.
/>
那么TypeScript将显示一个类型错误。
这很好,因为错误会在开发过程中被发现,而不会隐藏在代码库中。
2.类型化道具
在我看来,React从TypeScript获得的最好的好处是道具类型化。
对React组件进行类型化通常是一个两步过程。
A) 定义接口,描述该组件使用对象类型接受哪些道具。Props接口的一个好的命名规则是ComponentName +Props =ComponentNameProps
B) 然后使用该接口来注释功能组件函数内的道具参数。
例如,让我们注释一个接受2个道具的组件Message :text (一个字符串)和important (一个布尔值)。
tsx
interface MessageProps {
text: string;
important: boolean;
}
function Message({ text, important }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{text}
</div>
);
}
MessageProps 是描述该组件接受的道具的接口: 道具为 , 为 。text string important boolean
现在,当渲染组件时,你将不得不根据道具类型来设置道具值。
tsx
<Message
text="The form has been submitted!"
important={false}
/>
基本道具类型为不同种类的道具提出了类型。使用该列表作为灵感。
2.1 道具验证
现在,如果你碰巧为组件提供了错误的道具集,或者错误的值类型,那么TypeScript将在编译时警告你错误的道具值。
通常情况下,一个错误会在以下某个阶段被发现--类型检查、单元测试、集成测试、端到端测试、来自用户的错误报告--而你越早发现这个错误越好
如果Message 组件渲染的道具值是无效的。
tsx
<Message
text="The form has been submitted!"
important={0}
Type 'number' is not assignable to type 'boolean'.
/>
或没有道具:
tsx
<Message
Property 'text' is missing in type '{ important: true; }' but required in type 'MessageProps'.
important={true}
/>
那么TypeScript会对此发出警告。
2.2儿童道具
children 是React组件中的一个特殊道具:当组件被渲染时,它保存着开端和结束标签之间的内容: 。<Component>children</Component>
大多数情况下,children 道具的内容是一个JSX元素,它可以使用一个特殊的类型JSX.Element (在React环境中全局可用的类型)进行输入。
让我们稍微改变一下Message 组件,以使用children prop。
tsx
interface MessageProps {
children: JSX.Element | JSX.Element[];
important: boolean;
}
function Message({ children, important }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
看看接口中的children prop:它接受一个单一的元素JSX.Element 或一个元素数组JSX.Element[] 。
现在你可以用一个元素作为子元素来表示消息。
tsx
<Message important={false}>
<span>The form has been submitted!</span>
</Message>
或多个子元素: tsx
<Message important={false}>
<span>The form has been submitted!</span>
<span>Your request will be processed.</span>
</Message>
挑战:你将如何更新MessageProps 接口,以便同时支持一个简单的string 值作为子代?请在下面的评论中写出你的解决方案!
2.3 可选道具
要使一个道具在道具界面中成为可选,可以用一个特殊的符号标志? 。
例如,让我们把important 这个道具标记为可选的。
tsx
interface MessageProps {
children: JSX.Element | JSX.Element[];
important?: boolean;
}
function Message({ children, important = false }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
在MessageProps 接口里面,important 道具被标记为? -important?: boolean - 使得这个道具是可选的。
在Message 函数里面,我还为important 道具添加了一个false 的默认值:{ children, important = false } 。这将是默认值,如果important 道具没有被指明的话。
现在TypeScript允许你跳过important 这个道具。
tsx
<Message>
<span>The form has been submitted!</span>
</Message>
当然,如果你愿意,你仍然可以使用important 。
tsx
<Message important={true}>
<span>The form has been submitted!</span>
</Message>
3.返回类型
在前面的例子中,Message 函数并没有明确指出它的返回类型。这是因为TypeScript很聪明,可以推断出函数的返回类型 -JSX.Element 。
tsx
type MessageReturnType = ReturnType<typeof Message>;
type MessageReturnType = JSX.Element
在React功能组件的情况下,返回类型通常是JSX.Element 。
tsx
function Message({
children,
important = false
}: MessageProps): JSX.Element {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
有些情况下,组件在某些条件下可能什么都不返回。如果是这种情况,只需使用一个联合JSX.Element | null 作为返回类型。
tsx
interface ShowTextProps {
show: boolean;
text: string;
}
function ShowText({ show, text }: ShowTextProps): JSX.Element | null {
if (show) {
return <div>{text}</div>;
}
return null;
}
ShowText 如果 prop是 ,则返回一个元素,否则返回 。这就是为什么 函数的返回类型是一个union 。show true null ShowText JSX.Element | null
3.1 提示:强制执行返回类型
我的建议是强制每个函数明确指出返回类型。通过这样做,许多愚蠢的错误和错别字都可以被发现。
例如,如果你不小心在return 和返回的表达式之间设置了一个换行,那么明确指出的返回类型就会抓住这个问题。
tsx
function BrokenComponent(): JSX.Element {
return
Type 'undefined' is not assignable to type 'Element'.
<div>Hello!</div>;
}
(注意:当return 关键字和表达式之间有一个换行时,那么函数就会返回undefined ,而不是表达式。)
然而,如果没有指明返回类型,错误使用的return 就不会被TypeScript注意到(也不会被你注意到!)。
tsx
function BrokenComponent() {
return
<div>Hello!</div>;
}
那就祝你调试顺利吧!
4.总结
React组件可以从TypeScript中大大受益。
类型化组件对于验证组件的道具特别有用。通常,这是由定义一个接口来完成的,每个道具都有它的类型。
然后,当注解的组件渲染时,TypeScript会验证是否提供了正确的道具值。
在数据验证的基础上,类型可以成为元信息的一个重要来源,提供注释的函数或变量如何工作的线索。