01 类型声明

  1. // 先定义类型
  2. interface Basic {
  3. num: number;
  4. str: string | null;
  5. bol?: boolean;
  6. }
  7. interface Func {
  8. func(str: string): void;
  9. }
  10. interface Arr {
  11. str: string[];
  12. mixed: Array<string | number>;
  13. fixedStructure: [string, number];
  14. basics: Basic[];
  15. }
  16. // on(type: string, cb: Function): {}
  17. event.on('change', function() {});
  18. type Cb = () => void;
  19. on(type: string, cb: Cb);
  20. // 枚举类型
  21. enum Status {
  22. Draft,
  23. Published
  24. }
  25. // 也可指定值
  26. enum Status {
  27. Draft = 'Draft',
  28. Published = 'Published'
  29. }

(1) 基本类型

  1. // string
  2. let employeeName:string = 'John Smith';
  3. // boolean
  4. let isPresent:boolean = true
  5. // Array
  6. let fruits: string[] = ['Apple', 'Orange', 'Banana']
  7. // tuple
  8. let user: [number, string, boolean, number, string];// declare tuple variable
  9. let user = [1, "Steve", true, 20, "Admin"];// initialize tuple variable
  10. // Enum
  11. enum PrintMedia {
  12. Newspaper,
  13. Newsletter,
  14. Magazine,
  15. Book
  16. }
  17. // Union
  18. let code: (string | number);
  19. code = 123; // OK
  20. code = "ABC"; // OK
  21. code = false; // Compiler Error
  22. // any
  23. let something: any = "Hello World!";
  24. something = 23;
  25. something = true;
  26. // void
  27. function sayHi():void {
  28. console.log('Hi!')
  29. }
  30. let speech: void = sayHi();
  31. console.log(speech); // Output: undefined
  32. // Never
  33. function throwError(errorMsg: string): never {
  34. throw new Error(errorMsg);
  35. }
  36. function keepProcessing(): never {
  37. while (true) {
  38. console.log('I always does something and never ends.')
  39. }
  40. }

(2) 申明方式

  • Record 用这个来声明对象结构的类型
    用于定义一个javascript的对象,key是字符串,value是任意类型 const people:Record = { name: ‘chengfeng’, age: 10 }

  • Partial 作用是将传入的属性变为可选项. ```typescript interface iPeople { title: string; name: string; }

const people: Partial = { title: ‘Delete inactive users’, }; // true const people2: Partial = { title: ‘Delet’, name:’john’}; // true

  1. - **keyof 可以用来取得一个对象接口的所有 key 值.**
  2. ```typescript
  3. interface Foo {
  4. name: string;
  5. age: number
  6. }
  7. // T -> "name" | "age"
  8. type T = keyof Foo
  9. type Keys = "a" | "b"
  10. type Obj = {
  11. [p in Keys]: any
  12. }
  13. // Obj -> { a: any, b: any }
  • Readonly 作用是将传入的属性变为变成只读 ```typescript interface iPeople { title: string; name: string; }

const people: Readonly = { title: ‘todo list’, name: chenfeng; }

  1. - **Required 的作用是将传入的属性变为必选项 **
  2. ```typescript
  3. type Required<T> = { [P in keyof T]-?: T[P] };
  4. interface iPeople {
  5. title?: string;
  6. name?: string;
  7. }
  8. type Requiredmoe = Required<iPeople>
  9. const people1: iPeople = { title: 'ts' }; // OK
  10. const people22: Requiredmoe = { title: 'ts' }; // Error: property 'name' missing
  • PICK 从 T 中取出 一系列 K 的属性 【 解构 】 ```typescript type Pick = { [P in K]: T[P] };

interface User { age: number; name: string; keydirction:string; };

// 相当于: type PickUser = { age: number; name: string; } type PickUser = Pick

  1. - **Exclude 前面类型T 的与后面类型U 对比,过滤出前面T 独有的属性 [交集+ 减集]**
  2. ```typescript
  3. T extends U ? X : Y // 如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y
  4. type Exclude<T, U> = T extends U ? never : T;
  5. const str: Exclude<'a' | '1' | '2', 'a' | 'y' | 'z'> = '1';
  • Omit Pick & Exclude 去除掉 接口内的 某属性。 【减集】 ```typescript // Pick type Pick = { [P in K]: T[P] };

// Exclude type Exclude = T extends U ? never : T;

// Omit type Omit = Pick>

type Omit = Pick>;

interface User { id: number; age: number; name: string; }

type OmitUser = Omit; // 相当于: type OmitUser = { age: number; name: string; }

  1. ```typescript
  2. // 首先声明一下模块:
  3. declare module 'progressbar.js' {
  4. // 模块中暴露了 Circle 类
  5. export class Circle {
  6. constructor(container: HTMLElement, options: Options);
  7. }
  8. // 构造函数的 Options 需要单独声明
  9. interface Options {
  10. easing?: string;
  11. strokeWidth?: number;
  12. trailColor?: string;
  13. trailWidth?: number;
  14. }
  15. }

02 注释

(1) 文件顶部的注释,包括描述、作者、日期

  1. /**
  2. * @description xxxxxx
  3. * @author wuliang
  4. * @since 20/05/20
  5. */

(2) 模块的注释

  1. /**
  2. * 拷贝数据
  3. * @param {*} data 要拷贝的源数据
  4. * @param {boolean} [isDeep=false] 是否深拷贝,默认浅拷贝
  5. * @return {*} 返回拷贝后的数据
  6. */

(4) 变量注释

  1. interface IState {
  2. // 名字
  3. name: string;
  4. // 电话
  5. phone: number;
  6. // 地址
  7. address: string;
  8. }

03. 引用组件顺序

  • 先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 css 样式
  1. import * as React from 'react';
  2. import { Dropdown, Menu, Icon } from 'antd';
  3. import Header from './Header';
  4. import toast from 'common/toast';
  5. import './index.less';

04. 引号

  • 使用单引号,或者 es6 的反引号

05. 缩进

  • 使用两个空格
    const handleCheck = () => { onCancel && onCancel(); onClose && onClose();
  • 除了代码块的以外的每个表达式后必须加分号。

07. 括号

下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try, catch, finally, with。

  1. // not good
  2. if (condition) doSomething();
  3. // good
  4. if (condition) {
  5. doSomething();
  6. }

08. 空格

  • 二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
    // bad ++ x; y ++; z = x?1:2;
    // good ++x; y++; z = x ? 1 : 2;

  • 用作代码块起始的左花括号 { 前必须有一个空格。
    // bad if (condition){ }
    while (condition){ }
    function funcName(){ }
    // good if (condition) { }
    while (condition) { }
    function funcName() { }

  • if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
    // bad if(condition) { }
    while(condition) { }
    (function() { })();
    // good if (condition) { }
    while (condition) { }
    (function () { })();

  • 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。
    // bad var obj = { a : 1, b:2, c :3 };
    // good var obj = { a: 1, b: 2, c: 3 };

8. 换行

  • 每个独立语句结束后必须换行。
  • 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
    // bad var obj = { a: 1 , b: 2 , c: 3, };
    function test() { … } for (const key in object) { if (object.hasOwnProperty(key)) { const element = object[key];
    } } // good var obj = { a: 1, b: 2, c: 3, };
    function test() { … }
    for (const key in object) { if (object.hasOwnProperty(key)) { const element = object[key];
    } }

  • 下列关键字后:else, catch, finally 不需要换行
    // bad if (condition) { … } else { … }
    try { … } catch (e) { … } finally { … }
    // good if (condition) { … } else { … }
    try { … } catch (e) { … } finally { … }

9. 数组、对象

  • 对象属性名不需要加引号;
  • 对象以缩进的形式书写,不要写在一行;
  • 数组最后不要有逗号。
  • 对象最后要有逗号。

  • // bad const a = { ‘b’: 1 };
    const a = {b: 1};
    const a = { b: 1, c: 2 }; const arr = [1, 2, 3, 4,];
    // good const a = { b: 1, c: 2, };
    const arr = [1, 2, 3, 4];

10. 命名

  • 类名: 大驼峰式风格,字母和数字,例如:AbcTest。禁止汉字、特殊符号,禁止非大驼峰式风格。
  • 函数名: 小驼峰式风格,字母和数字,例如:abcTest。禁止汉字、特殊符号,禁止非小驼峰式风格,例如snake_case等。

  • 变量名: 同函数名。

  • 常量: 全大写风格,大写字母、数字和下划线,单词之间以下划线分隔,例如:ABC_TEST。禁止汉字、特殊符号、小写字母。

  • 使用 onXxx 形式作为 props 中用于回调的属性名称。
    interface IProps { onClose?: () => void; onOk?: (item: Record) => void; }

  • 组件内的事件函数使用 handle 开头尾,handleCheckBtn。
  • 使用 withXxx 形式的词作为高阶组件的名称。
  • 接口命名前面带上 I 表示 interface
    interface IProps {} interface IState {}

11. 类型断言

  1. // bad
  2. function getLength(something: string | number): number {
  3. return something.length;
  4. }
  5. // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
  6. // Property 'length' does not exist on type 'number'.
  7. // bad
  8. function getLength(something: string | number): number {
  9. if ((<string>something).length) {
  10. return (<string>something).length;
  11. } else {
  12. return something.toString().length;
  13. }
  14. }
  15. // good
  16. function getLength(something: string | number): number {
  17. if (typeof something === 'string') {
  18. return something.length;
  19. } else {
  20. return something.toString().length;
  21. }
  22. }

12. interface声明顺序

  1. interface iProps {
  2. readonly x: number;
  3. readonly y: number;
  4. name: string;
  5. age: number;
  6. height?: number;
  7. [propName: string]: any;
  8. }

14. ts一些好用的小tips

  • keyof
    interface iPeople { name: string; age: number }
    type T = keyof iPeople // -> “name” | “age”
  • in
    type Keys = “a” | “b” type Obj = { [p in Keys]: any } // -> { a: any, b: any }

15. 规范其他

  • 不要使用 var 声明变量
  • 不会被修改的变量使用 const 声明
  • 去除声明但未被引用的代码
  • 禁止在代码里使用 debug
  • 不允许有空的代码块

16. State & Props

  1. 仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state
  1. // bad
  2. constructor (){
  3. this.setState({ people: this.props.people })
  4. }
  5. // good
  6. state: IState = {
  7. people: {},
  8. };

17. 渲染默认值

  • 添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
    // bad render(){ {name} }
    // good render(){ {!!name || ‘—‘} }

  • 还有一种情况,就是本来后端应该返回一个数组给你,但是数据库取不到数据,可能后端给你返回了null,然后前端null.length。这样就gg了


  • // bad const { list, totalCount } = await getPeopleList(keyword, page, pageSize); list 可能是null或者undefined list.length将直接导致前端报错
    this.setState({ status: STATUS.READY, apps: list, total: totalCount, page: page, });
    // good const { list, totalCount } = await getPeopleList(keyword, page, pageSize); this.setState({ status: STATUS.READY, apps: list || [], total: totalCount || 0, page: page, });

18. 不确定的属性,最后却疯狂的用…访问不存在的属性

例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接data.dataList.forEach()

  1. // bad
  2. const data = await getPeopleList(keyword, page, pageSize);
  3. data.dataList.forEach() // 直接挂了
  4. // good
  5. const data = await getPeopleList(keyword, page, pageSize);
  6. if (data && data.dataList && Array.isArray(data.dataList) {
  7. data.dataList.forEach()
  8. }

19. 数据格式转换

  1. 把字符串转整型可以使用+号
    let maxPrice = +form.maxPrice.value; let maxPrice = Number(form.maxPrice.value);
  2. 转成 boolean 值用!!
    let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/);

20. 判断条件真假

js 中以下为假,其他情况为真

  • false
  • null
  • undefined
  • 0
  • ‘’ (空字符串)
  • NaN

21. 简单组件可以使用函数代替

  1. // bad
  2. class Listing extends React.Component {
  3. render() {
  4. return <div>{this.props.hello}</div>;
  5. }
  6. }
  7. // good
  8. function Listing({ hello }) {
  9. return <div>{hello}</div>;
  10. }

22. 对于常用的属性进行缓存

  1. // bad
  2. this.props.app.openid;
  3. this.state.time
  4. // good
  5. const { app } = this.props;
  6. const { time } = this.state;
  7. console.log(app.openid)

23. input 输入框使用 trim()

  1. // bad
  2. let searchContent = form.search.value;
  3. // good
  4. let searchContent = form.search.value.trim();

24. 使用 location 跳转前需要先转义

  1. // bad
  2. window.location.href = redirectUrl + '?a=10&b=20';
  3. // good
  4. window.location.href = redirectUrl + encodeURIComponent('?a=10&b=20');

25. 使用 react-router

  1. // bad
  2. import { withRouter, RouteComponentProps } from 'react-router-dom';
  3. export interface IProps extends RouteComponentProps<any> {}
  4. class App extends React.Component<IProps, AppStates> {}
  5. export default withRouter(App);
  6. // good
  7. import { withRouter, RouteComponentProps } from 'react-router-dom';
  8. class App extends React.Component<IProps & RouteComponentProps<{}>, AppStates> {}
  9. export default withRouter(App);

26. 同时开发,数据请求 api 目录 git 冲突目录方案

  • 在 api 目录下新建一个目录,目录对应一级 tab,这个目录内放置一个 index.js ,最后把二级 tab 组件所使用的 api 请求都在这个 index.js 内引入。
    // 目前
    |- api |- pageA.ts |- pageB.ts
    // 建议
    |- api |- pageA |- index.js |- aaa.js |- bbb.js |- pageB |- index.js |- aaa.js |- bbb.js |- ccc.js

27. 业务代码里面的异步请求需要 try catch

ajax 请求,使用 try catch,错误提示后端返回,并且做一些失败后的状态操作例如进入列表页,我们需要一个 loading 状态,然后去请求数据,可是失败之后,也需要把 loading 状态去掉,把 loading 隐藏的代码就写在 finally 里面。

  1. getStudentList = async () => {
  2. try {
  3. this.setState({
  4. loading: true,
  5. isEmpty: false
  6. });
  7. await getStudentList({});
  8. }
  9. catch (e) {
  10. // TODO
  11. console.log(e)
  12. }
  13. finally {
  14. // 失败之后的一些兜底操作
  15. this.setState({
  16. loading: false,
  17. isEmpty: true
  18. });
  19. }
  20. };

28. setState有三种用法

  1. // 对象
  2. this.setState({
  3. })
  4. // 函数,一般是用于在setState之前做一些操作
  5. this.setState(
  6. () => {
  7. // TODO
  8. console.log('')
  9. return {
  10. a:300
  11. }
  12. }
  13. )
  14. // 第二个参数,一般是用于在setState之后做一些操作
  15. this.setState({
  16. a:300
  17. }, () => {
  18. // TODO
  19. })

29. setState可能是同步的

  • setState 在react里的合成事件和钩子函数中是“异步”的。
  • setState 在原生事件和 setTimeout 中是同步的。

30. 不要在 setState 前面加 await

  • setState 前面也是可以带 await 的,会变成同步设置状态,但这是一种巧合,不确定未来哪个版本就不支持了,为了遵循 react 框架的设计原则,我们使用回掉函数的形式。

  • // bad func = async (name, value, status) => { await this.setState({ name }); // TODO };
  • 建议这种写法
    // good func = (name, value, status) => { this.setState( { name }, () => { // TODO } ); };

31. 阻止事件默认行为

  • 在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault 。

32. 在 componentWillUnmount 里面去除副作用的函数

  • 清除 EventListener
  • 中止数据请求
  • 清除定时器

33. key

  • 对于组件中的 key 优化,起到最大化重用 dom
    //bad this.state.dataAry.map((item, index) => { return ; });
    //good this.state.dataAry.map(item => );

34. for-in 中一定要有 hasOwnProperty 的判断(即禁止直接读取原型对象的属性)

  1. //bad
  2. const arr = [];
  3. const key = '';
  4. for (key in obj) {
  5. arr.push(obj[key]);
  6. }
  7. //good
  8. const arr = [];
  9. const key = '';
  10. for (key in obj) {
  11. if (obj.hasOwnProperty(key)) {
  12. arr.push(obj[key]);
  13. }
  14. }

35. 第三方库函数的使用

用 try catch 包裹,防止第三方库的出现错误,导致整个程序崩溃
/*

  • Echart 用于代绘制图表,但当其自身发生错误时,可能影响到业务代码的执行

*/ // bad const iniDom = document.getElementById(‘init-container’); const echartObj = echarts.init(iniDom); this.setState( { echartObj }, () => { const { echartObj } = this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); } );

// good try { const iniDom = document.getElementById(‘init-container’); const echartObj = echarts.init(iniDom); this.setState( { echartObj }, () => { const { echartObj } = this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); } ); } catch (error) { // TODO }

36. 在组件中获取真实 dom

  • 使用 16 版本后的 createRef()函数

  • class MyComponent extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); }
    render() { return ; }
    componentDidMount() { this.inputRef.current.focus(); } }

37. 减少魔法数字

  • 写代码的时候尽量减少一些未知含义的数字,尽量用英文单词。例如type === 0的时候做了一些操作,让人不知所以然。
    // bad if (type !== 0) { // TODO }

  • // good const STATUS: Record = { READY: 0, FETCHING: 1, FAILED: 2 };
    if (type === STATUS.READY) { // TODO }
    // best enum STATUS { // 就绪 READY = 0, // 请求中 FETCHING = 1, // 请求失败 FAILED = 2, }

38. Event 事件对象类型

很多小伙伴用了很久的ts,都不知道常用 Event 事件对象类型:

ClipboardEvent 剪贴板事件对象
DragEvent 拖拽事件对象
ChangeEvent Change 事件对象
KeyboardEvent 键盘事件对象
MouseEvent 鼠标事件对象
TouchEvent 触摸事件对象
WheelEvent 滚轮事件对象
AnimationEvent 动画事件对象
TransitionEvent 过渡事件对象

  1. import { MouseEvent } from 'react';
  2. interface IProps {
  3. onClick(event: MouseEvent<HTMLDivElement>): void;
  4. }

39. 使用私有属性取代state状态

对于一些不需要控制ui的状态属性,我们可以直接绑到this上, 即私有属性,没有必要弄到this.state上,不然会触发渲染机制,造成性能浪费 例如请求翻页数据的时候,我们都会有个变量。

50. 前端不要操作cookie

在做一些前后端鉴权的时候,后端应该开启domain,secure,httponly严格模式,禁止前端操作cookie,防止csrf攻击。