安装TS

npm i -g typescript

Tips: 因为使用的命令是tsc,这里安装的时候好多人都错误写成了 npm i -g tsc❎, 结果在使用的时候就会被提示,安装的不对,要重新安装。

TS转换JS

假设当前页面有index.ts文件,通过终端进入进入当前目录后,输入tsc index.ts即可将TS文件转化为JS文件,可以看到转化的结果index.js文件,可以看到转化后的结果。当前TS还支持指定输入输入路径等等一大堆配置,这里就不赘述了。

回顾JS基础类型

ES6的数据类型有:

• Boolean
• Number
• String
• Array
• Function
• Object
• Symbol
• undefined
• null

TS基础类型

• Boolean
• Number
• String
• Array
• Function
• Object
• Symbol
• undefined
• null
• void
• any
• never
• 元组
• 枚举
• 高级类型

可见,除了ES6中基础的类型外,TS有一些独特的类型void、any、never、元组、枚举,以及一些高级的用法,简记为高级类型

类型注解

类型注解是一种为变量添加类型约束的方式,相当强类型语言中的类型声明。强类型语言可以参考这里。

大白话:约定了什么类型,就只能给变量赋什么类型的值。

示例代码:

let age: number = 18

代码中的 :number 就是类型注解。

对已经声明好类型的变量,如果赋值不同类型的值,会在TS中报错。

数据类型

原始类型

// 原始类型
let bool: boolean = true
let num: number = 123
let str: string = 'abc'
// str = 23

// symbol
let s1: symbol = Symbol()
let s2 = Symbol()
console.log(s1 === s2) // => false

数组

两种定义方式,一种是直接类型+[], 另一种使用泛型Array<元素类型>。泛型代表一种元素类型,后面会具体说明。

let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3, "4"]

元祖

元祖:限制长度个数、类型一一对应,ts中自己实现的,内容固定,类型固定。
元祖会有越界问题,可以越界添加元素(不建议这么做),但是不可以访问。

let tuple: [number, string] = [0, '1']
// tuple.push(2)
// console.log(tuple)
// console.log(tuple[2]) // Tuple type '[string, number]' of length '2' has no element at index '2'

null、undefined

任何类型的子类型,如果strictNullChecks的值为true,但是tsconfig严格模式下,则不能把null 和 undefined赋值给其他类型。

Tip: strictNullChecks 在tsconfig.json中配置.

let name:number | boolean;
name = null;

void

只能接受null,undefined。一般用于函数的返回值。

let a:void;
a = undefined;

Tip: 严格模式下不能将null赋予给void.

any

不进行类型检测。

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object有相似的作用,就像它在其它语言中那样。 但是 Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

当你只知道一部分数据的类型时,any类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

let list: any[] = [1, true, "free"];

list[1] = 100;

never

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。

function error(message: string): never {
    throw new Error("err");
}
function loop(): never {
    while (true) { }
}
function fn(x:number | string){
    if(typeof x == 'number'){

    }else if(typeof x === 'string'){

    }else{
        console.log(x); // never
    }
}

object

表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。

使用object类型,就可以更好的表示像Object.create这样的API。例如:

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

枚举类型

为什么使用枚举?

// 实例 - 区分角色类型
function initByRole(role) {
	if(role === 1 || role === 2) {
		// do sth
	} else if (role === 3 || role === 4) {
		// do sth
	} else if (role === 5) {
		// do sth
	} else {
		// do sth
	}
}

问题:

可读性差:很难记住数字的含义
可维护性差:硬编码,牵一发动全身

枚举可以解决上述问题。

什么是枚举?有哪些特性?

枚举:一组有名字的常量集合,也可以理解为定义一些带名字的常量。

  • ts最终会被编译成JS,是没有类型的,枚举只是在开发时候使用
  • 写好以后,经过编译,会变成一个对象
  • 枚举可以支持反举,但是限于索引,会根据上一个的值进行自动推断
  • 大写是规范
  • 使用赋值声明(const)的话,不会生成一个对象更简洁

数字枚举 示例

// 数组枚举默认从0开始,但是可以指定初始值
// 注意:指定初始值只对当前和之后的枚举有效
enum Role {
	Reporter = 1,
	Developer,
	Maintainer,
	Owner,
	Guest
}

字符串枚举

enum Message {
	Success = '恭喜你,成功了',
	Fail = '抱歉,失败了'
}

异构枚举
从技术的角度来说,枚举可以混合字符串和数字成员,但一般不这么实现:

enum Answer {
    N,
    Y = 'yes'
}

反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字。 例如,在下面的例子中:

enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

TypeScript可能会将这段代码编译为下面的JavaScript:

var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a]; // "A"

生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。

要注意的是不会为字符串枚举成员生成反向映射。

枚举成员
每个枚举成员都带有一个值,它可以是 常量 或 计算 出来的。

enum Char {
	// const 枚举
	a,
	b = Char.a,
	c = 1 + 3,
	// computed 枚举
	d = Math.random(),
	e = '123'.length
}