React组件是返回React元素的JavaScript函数。这些决定了什么将被添加到DOM中。就像函数一样,React组件可以接收称为props的参数,这可以导致动态返回元素。
Props可以是任何数据类型,但组件的预期数据类型可能不同。例如,组件A可能期望一个name
参数,数据类型为string
,而组件B可能期望一个age
参数,数据类型为number
。C可能期望一个post
参数,其数据类型为object
,属性为title
和date
,而D可能期望一个onClick
参数,其数据类型为Function
。
我们如何指定我们的组件应该期望的数据类型?
有很多方法可以做到这一点,但本文重点介绍PropTypes和TypeScript。尽管有类似的目的,但这两种方法在工作方式上有所不同。我们将介绍这些工具是什么,了解它们的区别,最后,尝试一些使用它们的高级方法。
为了完整地理解这篇文章,需要事先了解PropTypes和TypeScript的知识。你还需要安装一个集成开发环境(IDE),如VS Code。
什么是PropTypes?
PropTypes是React应用程序中道具的一个运行时类型检查工具。自React 15.5以来,PropTypes工具可通过prop-types包获得。通过PropTypes,你可以定义组件中预期的道具类型,比如说明道具是必需的还是可选的。当你向一个组件传递不同的类型或跳过一个必需的道具时,你会在JavaScript控制台得到一个警告。
import React from 'react'
import PropTypes from 'prop-types'
const NotificationCard = ({ message, type, id }) => {
return <span>Notification</Notification>
}
NotificationCard.propTypes = {
message: PropTypes.string.isRequired,
type: PropTypes.oneOf(["error", "warning", "success"]),
id: PropTypes.string.isRequired
}
export default NotificationCard
在安装了React插件的情况下,IntelliSense会在你输入时显示一个自动完成的道具名称,并显示该道具的预期数据类型。这里有一个例子。
当我们传递一个意外的数据类型时会发生什么?或者当我们没有提供一个必需的道具时?
IDE并没有向我们显示任何错误或警告。现在,让我们试着运行这段代码。
浏览器控制台的截图显示,由于传递了错误的数据类型,message
的道具出现了意外的数据类型。如果你给message
,你会得到另一个错误,表明id
,一个必需的道具,没有被提供。
注意,PropTypes在生产应用中不提供警告;警告只在开发期间打印。如果在使用你的应用程序的生产版本时收到一个意外的类型,控制台将不会打印任何警告。
什么是TypeScript?
在React中使用TypeScript允许你向你的道具添加类型,这些类型将在编译时被验证。 时进行验证。TypeScript会编译回JavaScript,因为这是浏览器所理解的。
在你的React应用程序中使用TypeScript之前,你可能需要提供必要的依赖和配置。例如,如果你使用Create React App,你可以按照这个文档在你现有的应用程序中添加TypeScript支持。
这里是上述PropTypes定义的TypeScript解释。
import React from 'react'
import PropTypes from 'prop-types'
type Props = {
message: string;
type: "error" | "warning" | "success";
id: string;
}
const NotificationCard = ({ message, type, id }: Props) => {
return <span>Notification</span>
}
export default NotificationCard
使用TypeScript的IDE工具,当你传递一个意外的道具时,你会在IDE中得到即时的IntelliSense警告。让我们通过提供错误的数据类型来测试一下;我们会得到一个警告信息。
当我们为message
,提供正确的数据类型时,我们会收到这个通知。
如上所见,IntelliSense还提供了更多关于错误的信息,以帮助你解决这个问题。
使用TypeScript,当违反道具要求时,运行npm run build
会失败。
对比TypeScript和PropTypes
这两个工具都是用来对道具进行类型检查的。尽管TypeScript现在看起来是最好的选择,但选择一个将不可避免地导致权衡。这里有一些需要考虑的关键区别。
1.运行时和编译时的类型检查
PropTypes在运行时进行类型检查,而应用程序正在浏览器中运行。然而,TypeScript在编译时进行类型检查,当TypeScript代码被编译为JavaScript时。这是值得注意的,因为它影响了工具的使用方式。下面是一些例子。
来自API的数据。
TypeScript不能对来自API的数据进行类型检查。这种代码的编译不会成功,因为这些数据的内容只能在运行时知道。相反,如果违反了预期的类型,PropTypes会给出警告。
把PropTypes看作是一个工具,它的作用类似(不完全是)于。
...
if (typeof age !== number) {
console.warn("Age should have a number data type")
}
...
无论age
是硬编码还是从API得到的,类型检查器仍然运行。另一方面,TypeScript不会进入浏览器,因此将类型检查局限于硬编码数据。
构建一个组件库
如果你正在创建一个组件库,你很可能会将生产代码发布到包管理器中。在编译时,TypeScript将被转换为JavaScript。这意味着你的库的用户不能依赖你的TypeScript类型定义的道具,重申了PropTypes的相关性。PropTypes是正常的JavaScript代码,这意味着当你的库被使用时,验证也将成为可能。
2.语法和语义高亮
这两种工具都有插件,提供组件道具信息的自动补全。然而,TypeScript的高亮工具优于PropTypes的。你会在TypeScript IDE工具中发现许多功能,如VS Code、WebStorm和Atom。当没有提供预期的道具数据类型时,TypeScript会适当地突出你的组件,并提供对解决方案的洞察力。
3.TypeScript中的高级功能
你可以在TypeScript中为你的道具指定类型,有很多方法是PropTypes可能无法提供的。例如,TypeScript允许你将接口与类型结合起来,并执行条件类型检查,比如说明如果属性A为真,则必须同时提供属性C。
这里有一个TypeScript的高级用法的例子。
import React from "react";
import PropTypes from "prop-types";
type Props =
| {
type: "error";
message: "";
}
| {
type: "success";
};
const NotificationCard = (props: Props) => {
return <span>Notification</span>;
};
export default NotificationCard;
在上面的代码片段中,如果type
是error
,那么也需要一个message
属性。但是如果它是success
,那么我们就不需要指定另一个属性。
下面是当我们有一个error
的type
而不提供message
属性时,我们得到的警告。
请注意,第一个没有message
属性的NotificationCard
提供了警告,但第二个NotificationCard
的用法却没有。
享受两个世界的优点
正如我前面所说,在Type Script和PropTypes之间做出选择需要权衡。你想通过使用PropTypes保留TypeScript的功能,比如语法高亮?或者你宁愿在运行时放弃类型检查?
你的选择将受到你的应用程序的使用方式的影响。例如,如果你将你的应用程序构建为一个将被许多人使用的库,那么运行时类型检查就非常重要。
然而,你可能不需要牺牲任何功能--有一些方法可以同时享受运行时和编译时类型检查。
首先,你可以选择为你的应用程序编写类型定义和PropTypes定义。从长远来看,当你必须为你的应用程序中的每个组件都写这两个定义时,这将是很吃力的。让我们回顾一下两种方法,你可以写一个并自动生成另一个。
1.InferProps
InferPropTypes
从 [@types/prop-types](https://www.npmjs.com/package/@types/prop-types)
可以用来从PropTypes定义中创建类型定义。这里有一个例子。
import React from "react";
import PropTypes, { InferProps } from "prop-types";
const BlogCardPropTypes = {
title: PropTypes.string.isRequired,
createdAt: PropTypes.instanceOf(Date),
authorName: PropTypes.string.isRequired,
};
type BlogCardTypes = InferProps<typeof BlogCardPropTypes>;
const BlogCard = ({ authorName, createdAt, title }: BlogCardTypes) => {
return <span>Blog Card</span>;
};
BlogCard.propTypes = BlogCardPropTypes;
export default BlogCard;
下面是我们突出显示组件时的情况。
该组件现在有一个PropTypes定义,当TypeScript类型被违反时也会给出错误。如图所示,它说Property 'authorName' is missing
,但它没有说createdAt
缺少,因为我们没有在BlogCardPropTypes
对象中为该属性添加一个isRequired
。
2.babel-plugin-typescript-to-proptypes
你可以从TypeScript类型定义中生成PropTypes,使用 [babel-plugin-typescript-to-proptypes](https://www.npmjs.com/package/babel-plugin-typescript-to-proptypes)
.当你为你的道具指定类型时,该插件将这些类型定义转换为PropTypes定义。
例如,下面的代码。
import React from "react";
type Props = {
title: string;
createdAt: Date;
authorName: string;
};
const BlogCard = ({ title, createdAt, authorName }: Props) => {
return <span>Blog card</span>;
};
export default BlogCard;
将像这样写的那样工作。
import React from 'react'
import PropTypes from 'prop-types'
const BlogCard = ({ title, createdAt, authorName }) => {
return <span>Blog card</span>;
};
BlogCard.propTypes = {
title: PropTypes.string.isRequired,
createdAt: PropTypes.instanceOf(Date),
authorName: PropTypes.string.isRequired,
}
export default BlogCard
它将被相应地编译,从而使你的React应用程序在运行时具有类型检查能力。
总结
围绕PropTypes和TypeScript有很多误解。有些人会说一个很重要,但另一个不重要。对我来说,它们都很重要,但如果我不是在构建一个将被其他人使用的工具,我会更优先考虑TypeScript。
在这篇文章中,我们已经看了什么是PropTypes和TypeScript,它们在React应用程序中的相关性,它们的区别,以及它们可以一起使用的方式。
要继续阅读,请查看这个关于React PropTypes的广泛指南。
The postComparing TypeScript and PropTypes in React applicationsappeared first onLogRocket Blog.