作为 JavaScript
的超集。以后的 TypeScript
十分风行,TypeScript
解决了许多在 JavaScript
中编程的痛点,进步了健壮性、可读性、开发效率、开发体验,所以学好 TypeScript
是十分有必要的。
想要学好 TypeScript
离不开良好的编程标准和最佳实际。本文蕴含某些技巧在 JavaScript
中也能实用,当然在 JavaScript
中的开发标准仍然可能在 TypeScript
中应用,然而要转变到 TypeScript
开发中时要对以往的开发思维、习惯进行一些扭转。
语法
- 禁止 应用
//@ts-ignore
、any
。 - tsx 中 必须 应用
as
断言,因为<>
容易跟jsx
标签、泛型语法起抵触。 - 禁止 批改任何原生 js 内置原型
xxx.prototype
。 - 倡议 尽量不应用
unknown
、双重断言,请应用更精确的类型或类型守卫,不要滥用类型断言。 - 对一些援用类型数据进行批改时 倡议 要先进行浅 / 深拷贝、或对象解构再进行批改,防止毁坏原数据。优先应用对象开展运算符
...
来做对象浅拷贝而不是应用Object.assign
、Array.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.allSettled
比Promise.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'
};