翻译者:陈峻
无人知晓您与否知悉,由于JavaScript是一类致密类别的词汇,而且须要内存运转时(runtime),因此会引致轻微错误发展缓慢被发现,从而引致轻微的不良后果。而作为一个著名的JavaScript库,React虽然已是现今最盛行、且行业领跑的前端开发库了。它也承继了这类问题。
为此,没人明确提出了纯洁标识符的基本概念。它是一类意在提升应用软件标识符的产品质量和可移植性,以听众为服务中心的连续性程式设计艺术风格。他们常说,其他人都能编写出能让计算机系统认知的标识符,但只有杰出的开发者才能通过明晰简约的设计商业模式,科水狼更易人类文明写作、认知、修正和保护的纯洁标识符,以减少应用软件的投资成本,并消解控制技术负债。
上面,他们将如是说八种在选用React和TypeScript撰写纯洁标识符时,必不可少且新颖的商业模式。
1.选用预设形式引入React
请引入如下表所示标识符:
拷贝
import * as React from “react”;1.
前述标识符既单纯又蛮横。如果我们不该选用React的大部份文本不然,就没有必要性这般,而应选用如下表所示更快的预设引入商业模式:
拷贝
import React,{useContext, useState} from “react”;1.
选用这种方式,他们能从组件中按需重构出React文本,而无须引入大部份的组件。总之,更为重要的是:在选用该引入形式以后,他们须要依照如下表所示形式配置tsconfig.json文件:
拷贝
{“compilerOptions”:{ “esModuleInterop”: true }}1.2.3.4.5.
在前述配置标识符段中,他们通过将esModuleInterop设置true,以启用[allowSyntheticDefaultImports]。它会让TypeScript能支持他们的语法。
2.在运转时的实现以后声明类别
请看如下表所示的标识符:
拷贝
import React,{Component} from “react”;const initialState ={ count:1 }const defaultProps ={ name:”John Doe”}type State = typeof initialState;type Props ={ count?: number }& typeof defaultPropsclass Counter extends Component { static defaultProps = defaultProps; state = initialState;//…}1.2.3.4.5.6.7.8.9.10.
由于他们将运转时与编译时(compile-time)的声明区分开来,因此上面的标识符段看上去更加明晰、易读。这类声明类别被称为——编译类别的优先声明。
让他们再来看如下表所示的标识符:
拷贝
import React,{Component} from “react”;type State = typeof initialState;type Props ={ count?: number }& typeof defaultPropsconst initialState ={ count:1 }const defaultProps ={ name:”John Doe”}class Counter extends Component { static defaultProps = defaultProps; state = initialState;//…}1.2.3.4.5.6.7.8.9.10.
他们从标识符的第一行就能明晰地看到,开发者已经知悉了对应的API。接着,他们就将编译时与运转时的声明区分开来了。
3.始终为Children Prop(子属性)提供显式类别
在React.d.ts中,TypeScript须要将函数组件和类组件的Children Prop予以注解,以展示React是如何处理Children Prop的。为此,他们有必要性为Children Prop显式地提供一个类别,以便将“children”用于文本映射的场景中。总之,如果他们的组件无需选用文本映射不然,则能单纯地用never类别予以注释。请参考如下表所示标识符段:
拷贝
import React,{Component} from “react”;// Card.tsxtype Props ={ children: import(react).ReactNode}class Card extends Component{ render(){ const {children}= this.props; return
上面是一些用于注释Children Prop的其他有效类别:
ReactNode ReactChild ReactElement对于原语,他们能选用string number boolean对象和数组也是有效的类别never null undefined (注意:他们并不推荐选用null和undefined)
4.选用类别推断来定义组件状态或DefaultProps
请参考如下表所示标识符段:
拷贝
import React,{Component} from “react”;type State ={ count: number };type Props ={ someProps: string & DefaultProps;}type DefaultProps ={ name: string}class Counter extends Component{ static defaultProps: DefaultProps ={name:”John Doe”} state ={count:0}//…}1.2.3.4.5.6.7.8.9.10.11.12.13.14.
虽然前述标识符能被顺利执行,但他们有必要性对其进行重构和改进,以便TypeScript的类别系统能正确地推断出readonly类别(如DefaultProps和initialState),从而防止开发者意外地设置状态:this.state ={},而引起轻微错误。请参见如下表所示标识符:
拷贝
import React,{Component} from “react”;const initialState = Object.freeze({ count:0 })const defaultProps = Object.freeze({name:”John Doe”})type State = typeof initialState;type Props ={ someProps: string }& typeof defaultProps;class Counter extends Component{ static readonly defaultProps = defaultProps; readonly state ={count:0}//…}1.2.3.4.5.6.7.8.9.10.11.
在前述标识符中,通过冻结DefaultProps和initialState,TypeScript类别系统能将它们推断为Readonly类别。可见,通过对静态DefaultProps和其类中的Readonly状态予以标记,他们消解了上文提到的,由设置状态可能引起的运转时轻微错误。
5.选用类别别名而不是接口(interface)来声明属性和状态
虽然他们能选用interface,但是为了确保明晰的连续性,并应对无法选用interface的场景,他们应选用类别别名。例如,在前面的示例中,他们通过重构标识符,使得TypeScript的类别系统,能从实现中定义状态类别,从而正确地推断出只读类别。而在如下表所示标识符中,他们就无法针对其商业模式选用interface:
拷贝
// workstype State = typeof initialState;type Props ={ someProps: string }& typeof defaultProps;// throws errorinterface State = typeof initialState;interface Props ={ someProps: string }& typeof defaultProps;1.2.3.4.5.6.
此外,在不能选用由unions和intersection创建的types,去扩展interface时,他们也必须选用type的别名。
6.不要在接口/类别别名中选用方式声明
通常,只有大部份的类别和推理要素都以相同的形式被声明时,他们才能确保标识符中的商业模式连续性。然而,–strictFunctionTypes参数只能有效地比较两个函数,而非方式。总之,您也能参考如下表所示标识符段:
拷贝
// Dont do thisinterface Counter { start(count:number): string reset(): void}// Dointerface Counter { start:(count:number)=> string reset:()=> string}1.2.3.4.5.6.7.8.9.10.11.
7.不要选用FunctionComponent
请不要选用FunctionComponent (简写 FC ),来定义某个函数组件。通常,他们在将TypeScript与React一起选用时,对应的函数式组件能被写成如下表所示两种形式:
(1)常规性功能标识符:
拷贝
type Props ={ message: string };const Greeting =({ message }: Props)=>
(2)选用React.FC或React.FunctionComponent的标识符段(如下表所示所示):
拷贝
import React,{FC} from “react”;type Props ={ message: string };const Greeting: FC= (props)=>
可见,选用FC的优点包括:针对displayName、propTypes和DefaultProps等静态属性,提供了类别检查和自动完成。不过,根据经验,它对propTypes、contextTypes、以及displayName的defaultProps,可能会引致问题。此外,FC为Children Prop提供的隐式类别,也存在着一些已知的问题。总之,如前所述,组件API本来就应该是显式的,因此他们没有必要性将Children Prop设置为隐式类别。
8.不要将构造函数用于类组件
请选用新的类字段建议,而无须在JavaScript类中选用构造函数。毕竟,选用构造函数会涉及调用super()和传递props,这些都会引入无须要的板商业模式(plate)和复杂性。
他们能选用如下表所示标识符段中的类字段,来撰写更简约、更更易保护的React类组件:
拷贝
// Dont dotype State ={count: number}type Props ={}class Counter extends Component{ constructor(props:Props){ super(props); this.state ={count:0}}}// Dotype State ={count: number}type Props ={}class Counter extends Component{ state ={count:0}}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
由前述标识符可知,他们在选用类字段时,涉及到的boilerplate(锅炉板商业模式)越少,待处理的this变量也就越少。
9.不要在类中选用公共访问器(Public Accessor)
让他们来看如下表所示标识符:
拷贝
import { Component } from “react”class Friends extends Component { public fetchFriends (){} public render (){ return // jsx blob }}1.2.3.4.5.6.7.
在前述类中,由于public的大部份元素的运转时都是预设的,因此他们无需通过显式选用public关键字,来添加额外的boilerplate文件,只需如下表所示商业模式即可:
拷贝
import { Component } from “react”class Friends extends Component { fetchFriends (){} render (){ return // jsx blob }}1.2.3.4.5.6.7.
10.不要在组件类中选用私有访问器
让他们再来看如下表所示标识符:
拷贝
import {Component} from “react”class Friends extends Component { private fetchProfileByID (){} render (){ return // jsx blob }}1.2.3.4.5.6.7.8.
在上面的标识符中,私有访问器仅在编译时,将fetchProfileByID方式私有化(private)。而在运转时,fetchProfileByID方式仍然是公共的。
目前,他们有多种方式能将JavaScript类中的属性和方式设定为私有。上面的标识符段展示了其中的一类–选用下划线()的命名规则:
拷贝
import {Component} from “react”class Friends extends Component { fetchProfileByID (){} render (){ return // jsx blob }}1.2.3.4.5.6.7.8.
虽然前述方式并没有真正使得fetchProfileByID成为私有方式,但它很好地向其他开发者传达了他们的意图,即:任何指定的方式应该被视为私有方式。弱映射(weakmap)、符号和作用域变量(scoped variable)都是这般。正如下表所示面的标识符段所示,他们能通过新的ECMAScript类字段的“建议”,选用各种私有字段,来轻松地达到此目的:
拷贝
import {Component} from “react”class Friends extends Component {#fetchProfileByID (){} render (){ return // jsx blob }}1.2.3.4.5.6.7.
总之,更为重要的是,您须要选用TypeScript3.8及更高版本,才能支持私有字段的相关语法。
11.不要选用枚举(enum)
虽然enum是JavaScript中的保留字,但是选用enum并非标准化的JavaScript惯用商业模式。鉴于枚举已在C#和Java之类的程式设计词汇中被广泛选用,您能在此依照如下表所示标识符形式,选用编译类别的表述形式:
拷贝
// Dont do thisenum Response { Successful, Failed, Pending}function fetchData (status: Response): void =>{ // some code.}// Do thistype Response = Sucessful Failed Pendingfunction fetchData (status: Response): void =>{ // some code.}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
小结
毫无疑问,选用TypeScript会为您的标识符添加许多额外的boilerplate,不过总体说来仍然是利大于弊的。希望前述如是说的十一种有关React和TypeScript应用的最佳实践和JavaScript惯用商业模式,能让您的标识符更加明晰、更加易被保护。