作为 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'};