关于typescript:TypeScript-编程规范最佳实践

2次阅读

共计 3842 个字符,预计需要花费 10 分钟才能阅读完成。

作为 JavaScript 的超集。以后的 TypeScript 十分风行,TypeScript解决了许多在 JavaScript 中编程的痛点,进步了健壮性、可读性、开发效率、开发体验,所以学好 TypeScript 是十分有必要的。

想要学好 TypeScript 离不开良好的编程标准和最佳实际。本文蕴含某些技巧在 JavaScript 中也能实用,当然在 JavaScript 中的开发标准仍然可能在 TypeScript 中应用,然而要转变到 TypeScript 开发中时要对以往的开发思维、习惯进行一些扭转。

语法

  • 禁止 应用 //@ts-ignoreany
  • tsx 中 必须 应用 as 断言,因为 <> 容易跟 jsx 标签、泛型语法起抵触。
  • 禁止 批改任何原生 js 内置原型xxx.prototype
  • 倡议 尽量不应用unknown、双重断言,请应用更精确的类型或类型守卫,不要滥用类型断言。
  • 对一些援用类型数据进行批改时 倡议 要先进行浅 / 深拷贝、或对象解构再进行批改,防止毁坏原数据。优先应用对象开展运算符 ... 来做对象浅拷贝而不是应用 Object.assignArray.from
  • 倡议 应用可选链(ES11 新增的个性) ?. 代替 a && a.b && a.b.c 这样的代码。
  • 倡议 应用空值合并运算符 ?? 代替 const name = nickName || loginName || '' 这样的代码。

让泛型变量更加语义化,常见泛型变量如下:

T(Type):示意一个 TypeScript 类型
K(Key):示意对象中的键类型
V(Value):示意对象中的值类型
E(Element):示意元素类型

排版格调(可读性)

倡议 interface申明程序,只读参数放第一位,必选参数第二位,而后是可选参数,不确定参数放最初:

interface IProps {
  readonly x: number;
  readonly y: number;
  name: string;
  age: number;
  height?: number;
  [propName: string]: any;
}

倡议 应用 ===!== 而非 ==!=,如条件过于简单,倡议 封装判断条件,晋升可读性。

正确示例:

function canActivateService(subscription: Subscription, account: Account) {return subscription.isTrial || account.balance > 0}

if (canActivateService(subscription, account)) {// ...}

谬误示例:

if (subscription.isTrial || account.balance > 0) {// ...}

倡议 同个文件每个模块只容许 import 一次,有多个 import 请写在一起。- eslint: no-duplicate-imports

正确示例:

import foo, {
  named1,
  named2
} from 'foo'

谬误示例:

import foo from 'foo'
// … some other imports … //
import {named1, named2} from 'foo'

函数

  • 尽量减少函数入参,函数入参 不倡议 超过 4 个,超过 4 个请应用对象来聚合。
  • 尽量遵循繁多职责,一个函数只干一件事,否则可能函数过于简单,思考拆分逻辑。
  • 高内聚、低耦合。
  • 倡议 尽量将逻辑严密相干的代码放在一起。

倡议 通过 TypeScript 的类型别名形容对象入参,能够进一步提高可读性。

正确示例:

type TMenuOptions = {title: string, body: string, buttonText: string, cancellable: boolean};

function createMenu(options: MenuOptions) {// ...}

createMenu(
  {
    title: 'Foo',
    body: 'Bar',
    buttonText: 'Baz',
    cancellable: true
  }
);

倡议 应用参数默认值语法而不是批改函数参数。
正确示例:

function handleThings (opts = {}) {// ...}

谬误示例:

function handleThings (opts) {
  // 1. 不应该间接批改入参
  // 2. 如果 opts 为 falsy 值,opts 可能会获取不到你预期的值,所以会导致一些问题
  opts = opts || {}
  // ...
}

倡议 过滤掉你没思考到的状况。
例如一个函数,你只想操作字符串,那你就要在函数结尾就只容许参数是字符串。

正确示例:

function parse (str:string){if (typeof(str) === 'string' ) {...}
}

不倡议 函数操纵内部作用域的变量,防止副作用,否则不好了解,易呈现 bug。

禁止 应用eval()new Function(),会导致太多破绽。

倡议 应用 Async/Await 代替Promise 链式写法,进步可读性。

在并发编程中 Promise.allSettledPromise.all更好,allSettled能够获取到你想要的状态,区别:

在 Promise.all 其中的某个 promise 被 reject 时,将会失落所有 promise 后果,如果你想取得所有 promise 后果能够思考应用 Promise.allSettled,Promise.allSettled 会最终返回所有 promise 后果(包含 reject 的)在 Promise.all 其中的某个 promise 被 reject 时,将会失落所有 promise 后果,如果你想取得所有 promise 后果能够思考应用 Promise.allSettled,Promise.allSettled 会最终返回所有 promise 后果(包含 reject 的)

删除未应用的内容

不应用(多余)的变量、代码段、空行、正文、文件都 必须 删除,否则会对保护人员带来困惑。

缩小硬编码

倡议 缩小魔法数字、硬编码,应用 const 常量或枚举形式进行代替,难以了解或可能产生歧义的中央最好加上正文。

正文

  • 倡议 尽量让代码来解释本人。
  • 正文应解释代码的用意,而不是形容代码怎么实现的。
  • 保障正文与代码统一,禁止 产生误导。

TSDoc 文档正文

TSDoc 是标准化 TypeScript 源文件中应用的 doc 正文的倡议,不仅能够进步代码可读性,还可取得编辑器智能提醒。

一个罕用的函数至多须要具备的 TSDoc 标签:摘要、入参阐明、返回值阐明。

/**
 * x 和 y 的相加
 *
 * @remarks
 * The main documentation for an API item is separated into a brief
 * "summary" section optionally followed by an `@remarks` block containing
 * additional details.
 * 
 * @param {number} x - The x value.
 * @param {number} y - The y value.
 * @returns  {number} Sum of x and y
 */

const sum: (x: number, y: number) => number =  (x, y) =>  x + y ;

interface(接口)至多须要具备的 TSDoc 标签:摘要、类型。

export interface IOrder {

    /**
     * ID
     *
     * @type unit32
     */
    'id': number;

    /**
     * 反对的店铺
     *
     * @type string
     */
    'site': string;

    /**
     * 类型
     *
     * @type  'cash_refund' [现金退款] | 'customer_refund' [客户退款]
     */
    'type': TRefundType;
}

在库 / 包 /API 接口类型文档开发中要求 必须 应用 TSDoc 正文,业务型代码则 倡议 应用 TSDoc 正文进步可读性。

在给函数、对象属性、变量增加正文时,优先应用 TSDoc 格调正文代替单行 / 多行正文,否则应用一般正文将会失去编辑器的智能提醒成果。

枚举小技巧

获取枚举映射对象

如果想要失去一个映射 fruitListMap,并不需要新建一个,而是能够间接依据FruitEnum 的枚举来生成一个映射数组。

enum EFruitEnum {
    TOMATO = '1',
    BANANA =  '2',
    APPLE = '3'
}

const fruitListMap = Object.entries(EFruitEnum);

在 ts 编译阶段的枚举值打印进去是个单向映射对象(当是字符串枚举时是单向,否则的话会是双向映射),因而咱们就能够间接拿这个单向映射对象去干些事件,比方下拉框的数据起源:

console.log(Object.entries(TYPE.EFruitEnum).map(([value, label]) => ({label, value})));

[
    {
        "label": "1",
        "value": "TOMATO"
    },
    {
        "label": "2",
        "value": "BANANA"
    },
    {
        "label": "3",
        "value": "APPLE"
    }
]

对象的 key 是枚举

keyof 可获取一个接口中所有 Key 的联结类型,当须要某个类型必须是 A 枚举的中的键的时候能够应用 keyof typeof AEnum

type IFruitObj = Record<keyof typeof EFruitEnum, string>;
const obj: IFruitObj = { //Error: obj 中短少属性 "BANANA"
    TOMATO: 'toma',
    APPLE: '333'
};
正文完
 0