本文译自 https://www.sitepoint.com/react-with-typescript-best-practices/
如今, React
和 TypeScript
是许多开发人员正在使用的两种很棒的技术。但是把他们结合起来使用就变得很棘手了,有时很难找到正确的答案。不要担心,本文我们来总结一下两者结合使用的最佳实践。
在开始之前,让我们回顾一下 React
和 TypeScript
是如何一起工作的。React
是一个 “用于构建用户界面的 JavaScript
库” ,而 TypeScript
是一个 “可编译为普通 JavaScript
的 JavaScript
类型化超集” 。通过同时使用它们,我们实际上是使用 JavaScript
的类型化版本来构建 UI。
将它们一起使用的原因是为了获得静态类型化语言( TypeScript
)对 UI
的好处:减少 JS 带来的 bug,让前端开发更安全。
一个经常被提到的常见问题是 TypeScript
是否编译你的 React
代码。TypeScript
的工作原理类似于下面的方式:
因此,答案是肯定的!但是稍后,当我们介绍 tsconfig.json
配置时,大多数时候你都想使用 "noEmit": true
。这是因为通常情况下,我们只是利用 TypeScript
进行类型检查。
概括地说, TypeScript
编译你的 React
代码以对你的代码进行类型检查。在大多数情况下,它不会发出任何 JavaScript
输出。输出仍然类似于非 TypeScript React
项目。
是的, TypeScript
可以与 React
和 webpack
一起使用。幸运的是,官方 TypeScript
手册对此提供了配置指南。
希望这能使你轻而易举地了解两者的工作方式。现在,进入最佳实践!
我们研究了最常见的问题,并整理了 React with TypeScript
最常用的一些写法和配置。这样,通过使用本文作为参考,你可以在项目中遵循最佳实践。
配置是开发中最无趣但是最重要的部分之一。我们怎样才能在最短的时间内完成这些配置,从而提供最大的效率和生产力?我们一起来讨论下面的配置
tsconfig.json
ESLint
/ Prettier
VS Code
扩展和配置初始化一个 React/TypeScript
应用程序的最快方法是 create-react-app
与 TypeScript
模板一起使用。你可以运行以下面的命令:
npx create-react-app my-app --template typescript
这可以让你开始使用 TypeScript
编写 React
。一些明显的区别是:
.tsx
:TypeScript JSX
文件扩展tsconfig.json
:具有一些默认配置的 TypeScript
配置文件react-app-env.d.ts
:TypeScript
声明文件,可以进行允许引用 SVG
这样的配置幸运的是,最新的 React/TypeScript
会自动生成 tsconfig.json
,并且默认带有一些最基本的配置。我们建议你修改成下面的内容:
{
"compilerOptions": {
"target": "es5", // 指定 ECMAScript 版本
"lib": [
"dom",
"dom.iterable",
"esnext"
], // 要包含在编译中的依赖库文件列表
"allowJs": true, // 允许编译 JavaScript 文件
"skipLibCheck": true, // 跳过所有声明文件的类型检查
"esModuleInterop": true, // 禁用命名空间引用 (import * as fs from "fs") 启用 CJS/AMD/UMD 风格引用 (import fs from "fs")
"allowSyntheticDefaultImports": true, // 允许从没有默认导出的模块进行默认导入
"strict": true, // 启用所有严格类型检查选项
"forceConsistentCasingInFileNames": true, // 不允许对同一个文件使用不一致格式的引用
"module": "esnext", // 指定模块代码生成
"moduleResolution": "node", // 使用 Node.js 风格解析模块
"resolveJsonModule": true, // 允许使用 .json 扩展名导入的模块
"noEmit": true, // 不输出(意思是不编译代码,只执行类型检查)
"jsx": "react", // 在.tsx文件中支持JSX
"sourceMap": true, // 生成相应的.map文件
"declaration": true, // 生成相应的.d.ts文件
"noUnusedLocals": true, // 报告未使用的本地变量的错误
"noUnusedParameters": true, // 报告未使用参数的错误
"experimentalDecorators": true, // 启用对ES装饰器的实验性支持
"incremental": true, // 通过从以前的编译中读取/写入信息到磁盘上的文件来启用增量编译
"noFallthroughCasesInSwitch": true
},
"include": [
"src/**/*" // *** TypeScript文件应该进行类型检查 ***
],
"exclude": ["node_modules", "build"] // *** 不进行类型检查的文件 ***
}
其他建议来自 react-typescript-cheatsheet 社区
为了确保你的代码遵循项目或团队的规则,并且样式保持一致,建议你设置 ESLint
和 Prettier
。为了让它们配合的很好,请按照以下步骤进行设置。
1.安装依赖
yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react --dev
2.在根目录下创建一个eslintrc.js
文件并添加以下内容:
module.exports = {
parser: '@typescript-eslint/parser', // 指定ESLint解析器
extends: [
'plugin:react/recommended', // 使用来自 @eslint-plugin-react 的推荐规则
'plugin:@typescript-eslint/recommended', // 使用来自@typescript-eslint/eslint-plugin的推荐规则
],
parserOptions: {
ecmaVersion: 2018, // 允许解析最新的 ECMAScript 特性
sourceType: 'module', // 允许使用 import
ecmaFeatures: {
jsx: true, // 允许对JSX进行解析
},
},
rules: {
// 自定义规则
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
},
settings: {
react: {
version: 'detect', // 告诉 eslint-plugin-react 自动检测 React 的版本
},
},
};
3.添加 Prettier
依赖
yarn add prettier eslint-config-prettier eslint-plugin-prettier --dev
4.在根目录下创建一个 .prettierrc.js
文件并添加以下内容:
module.exports = {
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 120,
tabWidth: 4,
};
.eslintrc.js
文件:module.exports = {
parser: '@typescript-eslint/parser', // 指定ESLint解析器
extends: [
'plugin:react/recommended', // 使用来自 @eslint-plugin-react 的推荐规则
'plugin:@typescript-eslint/recommended', // 使用来自@typescript-eslint/eslint-plugin的推荐规则
'prettier/@typescript-eslint', // 使用 ESLint -config-prettier 禁用来自@typescript-eslint/ ESLint 与 prettier 冲突的 ESLint 规则
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: 2018, // 允许解析最新的 ECMAScript 特性
sourceType: 'module', // 允许使用 import
ecmaFeatures: {
jsx: true, // 允许对JSX进行解析
},
},
rules: {
// 自定义规则
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
},
settings: {
react: {
version: 'detect', // 告诉 eslint-plugin-react 自动检测 React 的版本
},
},
};
我们添加了 ESLint
和 Prettier
,下一步就是在保存时自动修复/美化我们的代码。
首先,安装 VSCode 的 ESLint extension
和 Prettier extension
。这将使 ESLint
与您的编辑器无缝集成。
接下来,通过将以下内容添加到您的中来更新工作区设置 .vscode/settings.json
:
{
"editor.formatOnSave": true
}
保存时, VS Code
会发挥它的魔力并修复您的代码。很棒!
React
的核心概念之一是组件。在这里,我们将引用 React v16.8
以后的标准组件,这意味着使用 Hook
而不是类的组件。
通常,一个基本的组件有很多需要关注的地方。让我们看一个例子:
import React from 'react'
// 函数声明式写法
function Heading(): React.ReactNode {
return <h1>My Website Heading</h1>
}
// 函数扩展式写法
const OtherHeading: React.FC = () => <h1>My Website Heading</h1>
注意这里的关键区别。在第一个例子中,我们使用函数声明式写法,我们注明了这个函数返回值是 React.ReactNode
类型。相反,第二个例子使用了一个函数表达式。因为第二个实例返回一个函数,而不是一个值或表达式,所以我们我们注明了这个函数返回值是 React.FC
类型。
记住这两种方式可能会让人混淆。这主要取决于设计选择。无论您选择在项目中使用哪个,都要始终如一地使用它。
我们将介绍的下一个核心概念是 Props
。你可以使用 interface
或 type
来定义 Props
。让我们看另一个例子:
import React from 'react'
interface Props {
name: string;
color: string;
}
type OtherProps = {
name: string;
color: string;
}
// Notice here we're using the function declaration with the interface Props
function Heading({ name, color }: Props): React.ReactNode {
return <h1>My Website Heading</h1>
}
// Notice here we're using the function expression with the type OtherProps
const OtherHeading: React.FC<OtherProps> = ({ name, color }) =>
<h1>My Website Heading</h1>
关于 interface
或 type
,我们建议遵循 react-typescript-cheatsheet
社区提出的准则:
interface
用于公共 API
的定义。State
和 Props
使用 type
,因为它更受约束。”让我们再看一个示例:
import React from 'react'
type Props = {
/** color to use for the background */
color?: string;
/** standard children prop: accepts any valid React Node */
children: React.ReactNode;
/** callback function passed to the onClick handler*/
onClick: () => void;
}
const Button: React.FC<Props> = ({ children, color = 'tomato', onClick }) => {
return <button style={{ backgroundColor: color }} onClick={onClick}>{children}</button>
}
在此 <Button />
组件中,我们为 Props
使用 type
。每个 Props
上方都有简短的说明,以为其他开发人员提供更多背景信息。?
表示 Props
是可选的。children props
是一个 React.ReactNode
表示它还是一个 React
组件。
通常,在 React
和 TypeScript
项目中编写 Props
时,请记住以下几点:
TSDoc
标记为你的 Props
添加描述性注释 /** comment */
。Props
使用 type
还是 interfaces
,都应始终使用它们。props
是可选的,请适当处理或使用默认值。幸运的是,当使用 Hook
时, TypeScript
类型推断工作得很好。这意味着你没有什么好担心的。举个例子:
// `value` is inferred as a string
// `setValue` is inferred as (newValue: string) => void
const [value, setValue] = useState('')
TypeScript
推断出 useState
钩子给出的值。这是一个 React
和 TypeScript
协同工作的成果。
在极少数情况下,你需要使用一个空值初始化 Hook
,可以使用泛型并传递联合以正确键入 Hook
。查看此实例:
type User = {
email: string;
id: string;
}
// the generic is the < >
// the union is the User | null
// together, TypeScript knows, "Ah, user can be User or null".
const [user, setUser] = useState<User | null>(null);
下面是一个使用 userReducer
的例子:
type AppState = {};
type Action =
| { type: "SET_ONE"; payload: string }
| { type: "SET_TWO"; payload: number };
export function reducer(state: AppState, action: Action): AppState {
switch (action.type) {
case "SET_ONE":
return {
...state,
one: action.payload // `payload` is string
};
case "SET_TWO":
return {
...state,
two: action.payload // `payload` is number
};
default:
return state;
}
}
可见,Hooks 并没有为 React
和 TypeScript
项目增加太多复杂性。
本节将介绍人们在将 TypeScript
与 React
结合使用时一些常见的坑。我们希望通过分享这些知识,您可以避免踩坑,甚至可以与他人分享这些知识。
最常见的情况之一是 onChange
在表单的输入字段上正确键入使用的。这是一个例子:
import React from 'react'
const MyInput = () => {
const [value, setValue] = React.useState('')
// 事件类型是“ChangeEvent”
// 我们将 “HTMLInputElement” 传递给 input
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
setValue(e.target.value)
}
return <input value={value} onChange={onChange} id="input-example"/>
}
有时,您希望获取为一个组件声明的 Props
,并对它们进行扩展,以便在另一个组件上使用它们。但是你可能想要修改一两个属性。还记得我们如何看待两种类型组件 Props
、type
或 interfaces
的方法吗?取决于你使用的组件决定了你如何扩展组件 Props
。让我们先看看如何使用 type
:
import React from 'react';
type ButtonProps = {
/** the background color of the button */
color: string;
/** the text to show inside the button */
text: string;
}
type ContainerProps = ButtonProps & {
/** the height of the container (value used with 'px') */
height: number;
}
const Container: React.FC<ContainerProps> = ({ color, height, width, text }) => {
return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}</div>
}
如果你使用 interface
来声明 props
,那么我们可以使用关键字 extends
从本质上“扩展”该接口,但要进行一些修改:
import React from 'react';
interface ButtonProps {
/** the background color of the button */
color: string;
/** the text to show inside the button */
text: string;
}
interface ContainerProps extends ButtonProps {
/** the height of the container (value used with 'px') */
height: number;
}
const Container: React.FC<ContainerProps> = ({ color, height, width, text }) => {
return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}</div>
}
两种方法都可以解决问题。由您决定使用哪个。就个人而言,扩展 interface
更具可读性,但最终取决于你和你的团队。
无论是用于诸如 Apollo
之类的 GraphQL
客户端还是用于诸如 React Testing Library
之类的测试,我们经常会在 React
和 TypeScript
项目中使用第三方库。发生这种情况时,你要做的第一件事就是查看这个库是否有一个带有 TypeScript
类型定义 @types
包。你可以通过运行:
#yarn
yarn add @types/<package-name>
#npm
npm install @types/<package-name>
例如,如果您使用的是 Jest
,则可以通过运行以下命令来实现:
#yarn
yarn add @types/jest
#npm
npm install @types/jest
这样,每当在项目中使用 Jest
时,就可以增加类型安全性。
该 @types
命名空间被保留用于包类型定义。它们位于一个名为 DefinitelyTyped
的存储库中,该存储库由 TypeScript
团队和社区共同维护。
由于信息量大,以最佳方式一起使用 React
和 TypeScript
需要一些学习时间,但是从长远来看,其收益是巨大的。在本文中,我们介绍了配置,组件,Props,Hook,常见用例和第三方库。尽管我们可以更深入地研究各个领域,但这应涵盖帮助您遵循最佳实践所需的 80%
。
如果您希望看到它的实际效果,可以在GitHub上看到这个示例。
https://github.com/jsjoeio/react-ts-example
本文由微信公众号code秘密花园原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/cjs0NpRb2JTSWXX3Gm933g
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。