比较React应用中的TypeScript和PropTypes

1,610 阅读8分钟

React组件是返回React元素的JavaScript函数。这些决定了什么将被添加到DOM中。就像函数一样,React组件可以接收称为props的参数,这可以导致动态返回元素。

Props可以是任何数据类型,但组件的预期数据类型可能不同。例如,组件A可能期望一个name 参数,数据类型为string ,而组件B可能期望一个age 参数,数据类型为number 。C可能期望一个post 参数,其数据类型为object ,属性为titledate ,而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会在你输入时显示一个自动完成的道具名称,并显示该道具的预期数据类型。这里有一个例子。

Intellisense Auto Complete Prop Name

当我们传递一个意外的数据类型时会发生什么?或者当我们没有提供一个必需的道具时?

Ide Display Wrong Prop

IDE并没有向我们显示任何错误或警告。现在,让我们试着运行这段代码。

Browser Console Screenshot Message Prop

浏览器控制台的截图显示,由于传递了错误的数据类型,message 的道具出现了意外的数据类型。如果你给message ,你会得到另一个错误,表明id ,一个必需的道具,没有被提供。

String Message Error Missing Prop

注意,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警告。让我们通过提供错误的数据类型来测试一下;我们会得到一个警告信息。

IDE Wrong Data Message

当我们为message ,提供正确的数据类型时,我们会收到这个通知。

Message Correct Data Type Notification

如上所见,IntelliSense还提供了更多关于错误的信息,以帮助你解决这个问题。

使用TypeScript,当违反道具要求时,运行npm run build 会失败。

Npm Run Build Fail Props Requirement Violation

对比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;

在上面的代码片段中,如果typeerror ,那么也需要一个message 属性。但是如果它是success ,那么我们就不需要指定另一个属性。

下面是当我们有一个errortype 而不提供message 属性时,我们得到的警告。

Type Error Message Property

请注意,第一个没有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;

下面是我们突出显示组件时的情况。

Infer Props Highlight Component

该组件现在有一个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.