1. 为什么要给React组件类型 ?
如果你在编写中型和大型的web应用程序,TypeScript很有用。注释变量、对象和函数在应用程序的不同部分之间创建了契约。
例如,假设我是一个在屏幕上显示格式化日期的组件的作者。
代码语言:javascript复制interface FormatDateProps {
date: Date
}
function FormatDate({ date }: FormatDateProps): JSX.Element {
return <div>{date.toLocaleString()}</div>;
}
根据FormatDateProps接口,组件FormatDate date prop的值只能是date的一个实例。这是一个约束条件。
为什么这个约束很重要?因为FormatDate组件在日期实例上调用方法date. tolocalestring(),并且日期prop必须是一个日期实例。否则组件将无法工作。
那么FormatDate组件的用户就必须满足这个约束,并且只提供date实例:
代码语言:javascript复制<FormatDate
date={new Date()}
/>
如果用户忘记了约束,例如提供一个字符串“Sep 28 2021”到日期prop:
代码语言:javascript复制<FormatDate
date="Sep 28 2021"
Type 'string' is not assignable to type 'Date'.
/>
那么TypeScript就会显示一个类型错误。
这很好,因为错误是在开发过程中捕获的,而不是隐藏在代码库中。
2. 约束 props
在我看来,React从TypeScript获得的最大好处是支持类型。
输入React组件通常需要两个步骤。
- 定义接口,描述组件使用对象类型接受什么 props。一个很好的props接口命名约定是ComponentName props = ComponentNameProps
- 使用接口标注功能组件功能内部的 props 参数。
例如,让我们注释一个接受两个props的组件Message: text(一个字符串)和important(一个布尔值):
代码语言:javascript复制interface MessageProps {
text: string;
important: boolean;
}
function Message({ text, important }: MessageProps) {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{text}
</div>
);
}
MessageProps是描述组件接受的props的接口:text 是字符串类型,important 的是 boolean 类型。
现在,当渲染组件时,你必须根据 props 类型设置 props 的值:
代码语言:javascript复制<Message
text="The form has been submitted!"
important={false}
/>
2.1 Props 验证
现在,如果你碰巧为组件提供了错误的props值类型,那么TypeScript会在编译时警告你错误的props值。
通常,错误是在以下阶段捕获的——类型检查、单元测试、集成测试、端到端测试、来自用户的错误报告——越早捕获错误越好!
如果Message组件呈现一个无效的prop值:
代码语言:javascript复制<Message
text="The form has been submitted!"
important={0}
Type 'number' is not assignable to type 'boolean'.
/>
或者没有 prop:
代码语言:javascript复制<Message
Property 'text' is missing in type '{ important: true; }' but required in type 'MessageProps'.
important={true}
/>
然后TypeScript会发出警告。
2.2 children prop
children是React组件中的一个特殊prop:当组件被渲染时,它保存了开始和结束标记之间的内容:
代码语言:javascript复制<Component>children</Component>
children prop 的内容大多是JSX元素,可以使用特殊类型JSX进行键入。元素(在React环境中全局可用的类型)。
让我们稍微更改Message组件以使用子组件prop:
代码语言:javascript复制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[]的数组。
现在你可以使用Jsx.Element 作为子元素来表示消息:
代码语言:javascript复制<Message important={false}>
<span>The form has been submitted!</span>
</Message>
或者多个子元素:
代码语言:javascript复制<Message important={false}>
<span>The form has been submitted!</span>
<span>Your request will be processed.</span>
</Message>
2.3 可选的 props
要使props 接口中的prop可选,用一个特殊的符号符号 ?
例如,让我们将重要的prop标记为可选的:
代码语言:javascript复制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 prop被标记为?——important?Boolean
, 即该 prop 可选。
在Message函数中,我还为的important 的 prop: {children, important = false}
添加了一个false默认值。如果没有指定值,这将是默认值。
现在TypeScript允许你跳过important 的 prop:
代码语言:javascript复制<Message>
<span>The form has been submitted!</span>
</Message>
3. Return type
在前面的示例中,Message函数没有显式地指示其返回类型。这是因为TypeScript很智能,可以推断出函数的返回类型——JSX.Element:
代码语言:javascript复制type MessageReturnType = ReturnType<typeof Message>;
type MessageReturnType = JSX.Element
我的建议是强制每个函数显式地指示返回类型。这样做可以发现许多愚蠢的错误和拼写错误。
在React函数组件的情况下,返回类型通常是JSX.Element:
代码语言:javascript复制function Message({
children,
important = false
}: MessageProps): JSX.Element {
return (
<div>
{important ? 'Important message: ' : 'Regular message: '}
{children}
</div>
);
}
在某些情况下,组件可能不返回任何内容。如果是这种情况,就使用联合JSX.Element | null
作为返回类型:
interface ShowTextProps {
show: boolean;
text: string;
}
function ShowText({ show, text }: ShowTextProps): JSX.Element | null {
if (show) {
return <div>{text}</div>;
}
return null;
}
如果show prop
为真,ShowText返回一个元素,否则返回null。这就是为什么ShowText函数的返回类型是一个联合JSX.Element。
总结
React组件可以从TypeScript中受益匪浅。
给组件规定类型对于验证组件的支持非常有用。通常,这是通过定义一个接口来实现的,每个prop都有自己的类型。
然后,当带注释的组件呈现时,TypeScript会验证是否提供了正确的prop值。
在数据验证的基础上,类型可以作为元信息的重要来源,提供注释函数或变量如何工作的线索。