考虑以下代码:
import React, {Component} from "react";
const initialState = { count: 1 }
const defaultProps = { name: "John Doe" }
type State = typeof initialState;
type Props = { count?: number } & typeof defaultProps
class Counter extends Component {
static defaultProps = defaultProps;
state = initialState;
// ...
}
如果我们将运行时声明和编译时声明分开,并且编译时声明位于运行时声明之前,那么里面的代码会更清晰,更具可读性。
考虑以下代码:
import React, {Component} from "react";
type State = typeof initialState;
type Props = { count?: number } & typeof defaultProps
const initialState = { count: 1 }
const defaultProps = { name: "John Doe" }
class Counter extends Component {
static defaultProps = defaultProps;
state = initialState;
// ...
}
现在,开发人员乍一看就知道组件 API 是什么样子,因为第一行代码清楚地表明了这一点。
据悉,我们还将编译时声明与运行时声明分开。
3.给孩子提供明确的道具
Typescript 通过在函数和类组件的 React.d.ts 中将childrenprops注释为可选来反映React如何处理childrenprops。
为此,我们需要显式地为子元素提供一个 props 类型。 此外,最好用孩子的道具类型明确注释它们。 这在我们想要使用子项进行内容投影的情况下非常有用。 如果我们的组件不使用它,我们可以简单地用 never 类型注释它。
考虑以下代码:
import React, {Component} from "react";
// Card.tsx
type Props = {
children: React.ReactNode
}
class Card extends Component<Props> {
render() {
const {children} = this.props;
return <div>{children}</div>;
}
}
以下是一些注释子项的 props 类型:
4.使用类型推断来定义组件状态或DefaultProps
看下面的代码:
import React, {Component} from "react";
type State = { count: number };
type Props = {
someProps: string & DefaultProps;
}
type DefaultProps = {
name: string
}
class Counter extends Component<Props, State> {
static defaultProps: DefaultProps = {name: "John Doe"}
state = {count: 0}
// ...
}
不过里面的代码是可以工作的,我们可以对其进行以下改进: 使 TypeScript 的类型系统能够正确推断只读类型,例如 DefaultProps 和initialState。
避免误设置state导致开发错误: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<Props, State> {
static readonly defaultProps = defaultProps;
readonly state = {count: 0}
// ...
}
在前面的代码中,通过冻结 DefaultProps 和initialState,TypeScript 类型系统现在可以将它们推断为只读类型。
此外,通过将类上的静态 defaultProps 和状态标记为只读,我们消除了前面提到的设置状态导致运行时错误的可能性。
5、声明Props/State时使用类型别名(type)而不是接口(interface)
尽管可以使用接口,但为了一致性和清晰性,最好使用类型,因为在某些情况下接口不起作用。 例如,在上面的示例中,我们构建了代码,以便 TypeScript 的类型系统可以通过从实现中定义状态类型来正确推断只读类型。 我们不能像下面的代码那样使用该模式的接口:
// works
type State = typeof initialState;
type Props = { someProps: string } & typeof defaultProps;
// throws error
interface State = typeof initialState;
interface Props = { someProps: string } & typeof defaultProps;
据报道,我们不能使用并集和交集创建的类型来扩展接口,因此在这种情况下,我们必须使用类型。
6. 不要在接口/类型中使用方法声明
这确保了我们的代码中的架构一致性,因为类型/接口推断的所有成员都以相同的方式声明。 另外,--strictFunctionTypes 仅在比较函数而不是方法时有效。 您可以从这个 TS 问题中得到进一步的解释。
// Don't do
interface Counter {
start(count:number) : string
reset(): void
}
// Do
interface Counter {
start: (count:number) => string
reset: () => string
}
7.不要使用FunctionComponent
或者干脆调用FC来定义一个功能组件。
使用 Typescript 和 React 时,可以通过两种方式编译功能组件:
和普通函数一样,就像下面的代码:
type Props = { message: string };
const Greeting = ({ message }: Props) => <div>{message}</div>;
使用 React.FC 或 React.FunctionComponent,如下所示:
import React, {FC} from "react";
type Props = { message: string };
const Greeting: FC = (props) => <div>{props}</div>;
使用 FC 提供了一些优点,例如类型检测和手动完成静态属性(例如 displayName、propTypes 和 defaultProps)。 它有一个已知的问题,会破坏 defaultProps 和其他属性:propTypes、contextTypes、displayName。
FC 还提供了隐式类型的子属性,该属性也存在已知问题。 据报道,如上所述,组件API应该是显式的,因此隐式类型的children属性并不是最好的。
8. 不要对类组件使用构造函数
通过新的类属性 [5] 提案,无需在 JavaScript 类中使用构造函数。 使用构造函数涉及调用 super() 并传递 props,这会引入不必要的样板文件和复杂性。
我们可以使用类数组来编写更简单、更容易维护的Reactclass组件,如下所示:
// Don't do
type State = {count: number}
type Props = {}
class Counter extends Component<Props, State> {
constructor(props:Props){
super(props);
this.state = {count: 0}
}
}
// Do
type State = {count: number}
type Props = {}
class Counter extends Component<Props, State> {
state = {count: 0}
}
在里面的代码中,我们看到使用类属性涉及的样板代码较少,因此我们不必处理 this 变量。
9.不要在类中使用public关键字
考虑以下代码:
import { Component } from "react"
class Friends extends Component {
public fetchFriends () {}
public render () {
return // jsx blob
}
}
由于类的所有成员在默认情况下且在运行时都是公共的,因此无需显式使用 public 关键字来添加额外的样板文件。 相反,请使用以下模式:
import { Component } from "react"
class Friends extends Component {
fetchFriends () {}
render () {
return // jsx blob
}
}
10.不要在组件类中使用private
考虑以下代码:
import {Component} from "react"
class Friends extends Component {
private fetchProfileByID () {}
render () {
return // jsx blob
}
}
在里面的代码中,private仅在编译时私有化fetchProfileByID方法,因为它只是一个Typescript模拟。 并且,在运行时,fetchProfileByID 方法始终是公共的。
有多种方法可以将 JavaScript 类属性/方法设置为私有。 使用逗号 (_) 变量命名约定,如下所示:
import {Component} from "react"
class Friends extends Component {
_fetchProfileByID () {}
render () {
return // jsx blob
}
}
虽然这实际上并没有使 fetchProfileByID 方法成为私有方法,但它很好地向其他开发人员传达了我们的意图,即指定的方法应被视为私有方法。 其他技术包括使用 WeakMap、Symbol 和作用域变量。
然而,通过新的 ECMAScript 类数组提案typescript读取json文件,我们可以通过使用私有数组轻松而优雅地实现这一点,如下所示:
import {Component} from "react"
class Friends extends Component {
#fetchProfileByID () {}
render () {
return // jsx blob
}
}
但是,TypeScript 在 3.8 及更高版本中支持私有数组的新 JavaScript 语法。
额外:不要使用枚举
尽管 enum 是 JavaScript 中的保留字,并且使用 enum 并不是标准的惯用 JavaScript 模式。
但是,如果您使用的是 C# 或 JAVA 这样的语言,以这种方式使用 enum 可能会非常迷人。 而且,还有更好的模式,例如使用编译类型文字,如下所示:
// Don't do this
enum Response {
Successful,
Failed,
Pending
}
function fetchData (status: Response): void => {
// some code.
}
// Do this
type Response = Sucessful | Failed | Pending
function fetchData (status: Response): void => {
// some code.
}
总结
毫无疑问,使用 Typescript 将为您的代码节省大量额外的样板,而且其好处是非常值得的。
为了让你的代码更干净、更好,不要忘记实现一个健壮的 TODO/issue[6] 过程。 它将帮助您的工程团队了解技术债务、就代码库问题进行协作并更好地规划冲刺。
本文翻译自:
参考
[1]
干净的代码:
[2]
Stepsize 的 VSCode:
[3]
JetBrains:
[4]
允许合成默认导入:
[5]
类属性:#consensus-in-tc39
[6]
待办事项/问题:
”
如果您看完文章后有什么意见,欢迎在留言区交流。 如果您觉得文章对您有帮助,请关注嘉三联(点赞、观看、分享)。 您对作者的每一次支持都是对作者的致敬。 前进的动力。
””
如果你想加入后台交流群typescript读取json文件,或者想通过其他方式与作者交流,可以在陌陌上加我:
”