TS规范

155 阅读5分钟

TS规范

基本规范

命名,声明

  • 使用PascalCase命名类,枚举,接口,组件名
  • 使用camelCase命名函数名,属性,本地变量名
  • 勿将C#的规范套用到TS中
  • 类型定义需在文件顶部声明
  • 文件名中使用小写-来分隔多个单词,例如home-api
  • 基本类型使用string,number等而非String,Number这类的装箱类型
  • 充分使用泛型和类型运算避免多余类型标记
  • 减少或者规避any的滥用,any等于放弃了类型检测,等于掩盖了类型设计
  • 推荐使用扩展性更强的interface,接口创建一个单一的平面对象类型来检测属性冲突,交叉类型是递归进行合并
  • 保持一种扩展形式,不要或减少interfacetype互相扩展的混用情况
  • 避免使用Object来包裹其他类(String,Number等)
  • 优先使用类型声明而非类型断言
  • 使用Index Signature来表示动态数据,并尽可能细化来表示类型安全
  • 优先使用Arrays,Tuple,ArrayLike而非number index signatures

  • 成员顺序按照 静态>实例属性>方法公开>保护>私有
  • 始终定义修饰符
  • 避免使用TS构造函数来定义属性
  • 使用Error相关类表示异常

模块

  • import顺序按照 npm模块自我编写模块相对路径,每组用空格进行分隔
  • 若类型被多个模块共享,则提取到types.ts中,全局类型,变量统一写在global.d.ts
  • 避免使用require导致TS检查被绕过
  • 一个模块进定义一个类,文件名需和类名相同

函数

  • 若无需返回值,返回类型声明为void*,而非any**
  • 若函数参数类型变化多样,尝试使用可选参数联合类型来表示参数类型,避免定义多个函数重载
  • 使用箭头函数绑定上下文;使用箭头函数代替匿名函数表达式
  • 回调函数请勿包含可选参数
  • 为回调函数提供this的类型
  • 使用typeof fn来标注增强的函数类型
  • 使用async/await来取代Promise
  • 使用undefined,而非null

Interface && Type 对比

  • 两者十分相似,大部分情况下可以混用,关键区别是type不可以被重新打开并增加新属性,而interface总是可以拓展
  • 官方推荐总是优先使用interface
  • 如果定义原始基础类型的别名,使用type
  • 如果定义函数类型,联合类型,使用type
  • 对于所有object的类型,使用interface,并且interface更方便做声明合并

其他

  • 注释代码使用TSDoc进行规范
  • 应和其他强类型语言一样,假设 Array 是不可变数据结构(Immutable)
  • 总是使用{}来包裹循环体和条件语句
  • 使用对象展开,数组展开来取代Object.assign和Array.prototype.concat
  • 使用readonly来避免mutation造成的错误
  • 要一直致力于减少nullundefined的影响范围
  • 优先使用union of interface而不是interfaces of unions
  • 优先使用unknown而非any
  • private在运行时并不能阻止外部用户的访问

TSLint

TSLint官网,建议在项目中添加TSLint来进一步约束规范,配置文件中的核心项目有以下:

  • extends?: string | string[] 预设配置的文件名称,也可以是配置文件的引用路径
  • rulesDirectory?: string | string[] 自定义规则的目录路径,可以用一个数据来描述多个路径
  • rules?: { [name: string]: RuleSetting } 规则的详细配置映射
  • defaultSeverity?: "error" | "warning" | "off" 严重程度设置

小Tips

  • 使用type-converage去测试type的覆盖率
  • 将@types的依赖放置在devDependencies中
  • 使用TSDOC去注释导出的函数,class,types

在 Vue 中使用 TS

在 React 中使用 TS

安装

npm i -D @types/react @types/react-dom @types/react-redux

"react" - `@types/react`  
"react-dom" - `@types/react-dom`  
"redux" - (types included with npm package)*  
"react-redux" - `@types/react-redux`  

React 类型表

React.FC<Props> | React.FunctionComponent<Props>

方法组件

const MyComponent: React.FC<Props> = ...

React.Component<Props, State>

类组件

class MyComponent extends React.Component<Props, State> { ...

React.ComponentType<Props>

方法组建 类组件 联合类型

const withState = <P extends WrappedComponentProps>(
  WrappedComponent: React.ComponentType<P>,
) => { ...

React.ComponentProps<typeof XXX>

获取指定组件的Props类型

type MyComponentProps = React.ComponentProps<typeof MyComponent>;

React.ReactElement | JSX.Element

Reac基本元素类型,表示了原始Dom元素

const elementOnly: React.ReactElement = <div /> || <MyComponent />;

React.ReactNode

任何可能的React节点类型

const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => ...

React.CSSProperties

JXS中的样式对象类型(用于行内样式)

const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...

React.XXXHTMLAttributes<HTMLXXXElement>

指定HTML元素的HTML属性类型

const Input: React.FC<Props & React.InputHTMLAttributes<HTMLInputElement>> = props => { ... }

<Input about={...} accept={...} alt={...} ... />

React.ReactEventHandler<HTMLXXXElement>

范型通用事件处理类型

const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... } 

<input onChange={handleChange} ... />

React.XXXEvent<HTMLXXXElement>

表示了更多的指定事件类型, 一些常见的有: ChangeEvent, FormEvent, FocusEvent, KeyboardEvent, MouseEvent, DragEvent, PointerEvent, WheelEvent, TouchEvent.

const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... }

<div onMouseMove={handleChange} ... />

代码示例

Function组件

import * as React from 'react';

type Props = {
  label: string;
  count: number;
  onIncrement: () => void;
};

export const FCCounter: React.FC<Props> = props => {
  const { label, count, onIncrement } = props;

  const handleIncrement = () => {
    onIncrement();
  };

  return (
    <div>
      <span>
        {label}: {count}
      </span>
      <button type="button" onClick={handleIncrement}>
        {`Increment`}
      </button>
    </div>
  );
};

Class组件示例

import * as React from 'react';

type Props = {
  label: string;
};

type State = {
  count: number;
};

export class ClassCounter extends React.Component<Props, State> {
  readonly state: State = {
    count: 0,
  };

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    const { handleIncrement } = this;
    const { label } = this.props;
    const { count } = this.state;

    return (
      <div>
        <span>
          {label}: {count}
        </span>
        <button type="button" onClick={handleIncrement}>
          {`Increment`}
        </button>
      </div>
    );
  }
}

tsconfig示例

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true, /* allows javaScript files to be compiled */
    "skipLibCheck": true, /* skip type checking of all declaration files */
    "esModuleInterop": true, /* disables namespace imports (import * as fs from "fs") and enables CJS/AMD/UMD style imports (import fs from "fs") */
    "allowSyntheticDefaultImports": true, /* allows default imports from modules with no default export */
    "strict": true, /* enables all strict type checking options */
    "module": "esnext", /* specifies module code generation */
    "moduleResolution": "node", /* resolves modules using Node engine */
    "isolatedModules": true, /* unconditionally emits imports for unresolved files */
    "resolveJsonModule": true, /* includes modules imported with .json extension */
    "noEmit": true, /* Not to compile code, only performs type checking */
    "jsx": "react", /* Support JSX in .tsx files (This one comes in handy sometimes) */
    "sourceMap": true, /* generates corrresponding .map file */
    "declaration": true, /* generate corresponding .d.ts file */
    "noUnusedLocals": true, /* reports errors on unused locals (suggested for clean code writing) */
    "noUnusedParameters": true, /* report errors on unused parameters (again, suggested for clean code writing) */
  },
  "include": [
    "src/**/*" // *** The files TypeScript should type check ***
  ],
  "exclude": ["node_modules", "build"] // *** The files to not type check ***
}

相关链接

**