一、前言
本文基于开源项目:
前阵子有小伙伴跟广东靓仔说自己在日常项目开发过程中有使用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[] = [1, 2, 3]; // 数组泛型,Array<元素类型> let list: Array<number> = [1, 2, 3];
5、Tuple
// 声明一个元组 let x: [string, number]; // 正确的初始化 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
: 对象类型,设置编译的选项,不设置则使用默认配置,配置项比较多,后面再列
t
ypeAcquisition
: 对象类型,设置自动引入库类型定义文件(.d.ts
)相关,该对象下面有 3 个子属性分别是:
enable
: 布尔类型,是否开启自动引入库类型定义文件(.d.ts
),默认 false
include
: 数组类型,允许自动引入的库名,如:["jquery", "lodash"]
exculde
: 数组类型,排除的库名
如不设定 files
和 include
,ts 默认是 exclude
以外的所有的以.ts
和.tsx
结尾的文件。如果,同时设置 files
的优先级最高,exclude
次之,include
最低。
上面都是文件相关的,编译相关的都是靠 compilerOptions
设置的,接着就来看一看。
属性名 |
值类型 |
默认值 |
描述 |
allowJs | boolean | false | 编译时,允许有 js 文件 |
baseUrl | string | 与 path 一同定义模块查找的路径 |
|
declaration | boolean | false | 生成 .d.ts 定义文件 |
jsx | string | "preserve" | jsx 的编译方式 |
noImplicitAny | boolean | false | 不允许隐式
如果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其实对于规模较大的前端团队来说,还是很有必要的。