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