日常项目开发使用typescript

前端大神之路

共 7593字,需浏览 16分钟

 · 2021-06-22

本文适合日常项目开发使用到TS、以及对TS感兴趣的小伙伴阅读。

欢迎关注前端早茶,与广东靓仔携手共同进阶~

作者:广东靓仔

一、前言

本文基于开源项目:

https://github.com/microsoft/TypeScript

https://www.typescriptlang.org/docs/

    前阵子有小伙伴跟广东靓仔说自己在日常项目开发过程中有使用typescript,不过仅仅只是使用了基础的类型声明,有些时候项目时间赶甚至写了很多any。
    这里广东靓仔整理了一些日常项目开发使用typescript的小技巧,希望对小伙伴有所帮助。

二、TS基础知识

    Typescript的基础知识我们要牢牢掌握,有一句老话,楼房建得高不高,地基很重要。
    Typescript的基础类型基本都是小写字母开头的(除了Array),很多小伙伴忽略了这点。相信有小伙伴有使用过Number ,TS指导建议我们不要这样使用。我们都知道在JavaScript代码中几乎从不使用的非原始装箱对象。
/* WRONG */ 
function reverse(s: String): String
请使用类型数字,字符串和布尔值。

/* OK */ 
function reverse(s: string): string

/* OK */ 
let decLiteral: number = 6;
基础类型:

1、boolean

2、number

3、string

4、[]、Array<>

// 在元素类型后面接上[],表示由此类型元素组成的一个数组
let list: number[] = [123];
// 数组泛型,Array<元素类型>
let list: Array<number> = [123];

5、Tuple 

// 声明一个元组
let x: [stringnumber];
// 正确的初始化
x = ['广东'10]; // OK
// 错误的初始化
x = [10'靓仔']; // Error

6、enum

const enum Directions {
  Up,
  Down,
  Left,
  Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]

7、any ---- 尽量不使用

8、void

9、undefined和null

10、never

function error(message: string): never {
    throw new Error(message);
}

11、类型断言:<>、as

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构

温馨提示我们定义变量的时候,尽量使用let

三、tsconfig.json 字段说明

tsconfig.json 所包含的属性并不多,只有 7 个。   

tsconfig可以到http://json.schemastore.org/tsconfig查看,下面我们重点讲讲字段。

files数组类型,用于表示由 ts 管理的文件的具体文件路径

exclude数组类型,用于表示 ts 排除的文件(2.0 以上支持 Glob)

include数组类型,用于表示 ts 管理的文件(2.0 以上)

compileOnSave布尔类型,用于 IDE 保存时是否生成编译后的文件

extends字符串类型,用于继承 ts 配置,2.1 版本后支持

compilerOptions对象类型,设置编译的选项,不设置则使用默认配置,配置项比较多,后面再列

typeAcquisition对象类型,设置自动引入库类型定义文件(.d.ts)相关,该对象下面有 3 个子属性分别是:

    enable: 布尔类型,是否开启自动引入库类型定义文件(.d.ts),默认 false

    include数组类型,允许自动引入的库名,如:["jquery", "lodash"]

    exculde数组类型,排除的库名

如不设定 files 和 include,ts 默认是 exclude 以外的所有的以 .ts  .tsx 结尾的文件。如果,同时设置 files 的优先级最高,exclude 次之,include 最低。

上面都是文件相关的,编译相关的都是靠 compilerOptions 设置的,接着就来看一看。

属性名

值类型

默认值

描述

allowJsbooleanfalse编译时,允许有 js 文件
baseUrlstring
与 path 一同定义模块查找的路径
declarationbooleanfalse生成 .d.ts 定义文件
jsxstring"preserve"jsx 的编译方式
noImplicitAnybooleanfalse不允许隐式 any,如果true,函数的形参必须带类型,如果叫不出class名的js对象,那就得any,比如(d:any)=>{}

如果false,函数的样子更像js  (d)=>{

............
............
一般情况下,tsconfig.json 文件只需配置 compilerOptions 部分。
{
  "compilerOptions": {
    "allowSyntheticDefaultImports"true// 允许引入没有默认导出的模块
    "module""es2015",
    "removeComments"true,
    "preserveConstEnums"true,
    "sourceMap"true,
    "strict"true,
    "target""es5",
    "lib": [
      "dom",
      "es5",
      "es2015"
    ]
  }
}
allowSyntheticDefaultImports 是使用 vue 必须的,而设置 module 则是让模块交由 webpack 处理,从而可以使用 webpack2 的摇树。另外,加上allowJs,这样就可以一点点将现有的 js 代码转换为 ts 代码了。
如果,在 webpack 中设置过 resolve -> alias,那么,在 ts config 中也需要通过 baseUrl + path 的方式来定义模块查找的方式。
<a name="tslint"></a>

四、ts使用小技巧

1.注释

我们平时开发过程可以通过 /** */ 形式的注释可以给 TS 类型做标记提示,这样子编辑器会有更好的提示。

/** This is a cool guy. */
interface Person {
  /** This is name. */
  name: string,
}

const p: Person = {
    name: 'cool'
}

当我们鼠标移动到person会有tooltip,一眼就看出来是什么了。

2.接口继承

和类一样,接口也是可以相互继承。让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;

一个接口可以继承多个接口,创建出多个接口的合成接口。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

3interface & type

TypeScript 定义类型的两种方式:接口(interface)和 类型别名(type alias)。

比如下面的 Interface 和 Type alias 的例子中,除了语法,意思是一样的:

Interface

interface Place {
  x: number;
  y: number;
}

interface SetPlace {
  (x: number, y: number): void;
}

Type alias

type Place = {
  x: number;
  y: number;
};

type SetPlace = (x: number, y: number) => void;

而且两者都可以扩展,但是语法有所不同。此外,请注意,接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。

4. typeof

typeof 可以获取一个对象/实例的类型

typeof 只能用在具体的对象上,这与 js 中的 typeof 是一致的,并且它会根据左侧值自动决定应该执行哪种行为。

interface Opt {
  timeout: number
}
const defaultOption: Opt = {
  timeout: 500
}

有时候可以反过来:

const defaultOption = {
  timeout: 500
}
type Opt = typeof defaultOption

5. 运算符

非空断言运算符 !

这个运算符可以用在变量名或者函数名之后,用来强调对应的元素是非 null|undefined 的。

function onClick(callback?: () => void{
  callback!();  // 参数是可选入参,加了这个感叹号!之后,TS编译不报错
}

    这个符号的场景,特别适用于我们已经明确知道不会返回空值的场景,从而减少冗余的代码判断,如 React 的 Ref。

function Demo(): JSX.Elememt {
  const divRef = useRef<HTMLDivElement>();
  useEffect(() => {
    divRef.current!.scrollIntoView(); 
    // 当组件Mount后才会触发useEffect,故current一定是有值的
  }, []);
  return <div ref={divRef}>Demo</div>
}

可选链运算符 ?.

?.这个是开发者最需要的运行时(当然编译时也有效)的非空判断。

obj?.prop
obj?.[index]
func?.(args)

?.用来判断左侧的表达式是否是 null | undefined,如果是则会停止表达式运行,可以减少我们大量的&&运算。

比如我们写出a?.b时,编译器会自动生成如下代码

a === null || a === void 0
 ?
  void 0
 :
  a.b;

这里涉及到一个小知识点:undefined这个值在非严格模式下会被重新赋值,使用void 0必定返回真正的 undefined。

空值合并运算符 ??

??与||的功能是相似的,区别在于??在左侧表达式结果为 null 或者 undefined 时,才会返回右侧表达式。

比如我们书写了let b = a ?? 10,生成的代码如下:

let b = a !== null && a !== void 0
 ?
  a
 :
  10;

而 || 表达式,大家知道的,则对 false、''、NaN、0 等逻辑空值也会生效,不适于我们做对参数的合并。

数字分隔符_

let num:number = 1_2_345.6_78_9

_可以用来对长数字做任意的分隔,主要设计是为了便于数字的阅读,编译出来的代码是没有下划线的。

五、总结

    typescript我们可以在Vue中引入,广东靓仔之前写过Vue+ts+vuex的相关文章,我们可以使用vue 支持 jsx,进而使用render jsx。当然我们也可以在React中引typescript
    总体来说ts其实对于规模较大的前端团队来说,还是很有必要的。

关注我,一起携手进阶

欢迎关注前端早茶,与广东靓仔携手共同进阶~

浏览 8
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报