Typescript

01-安装 Typescript

安装Typescript

1
2
3
4
5
6
7
8
9
npm install -g typescript
# 或者使用cnpm进行安装
cnpm install -g typescript
tsc -v 检测是否安装成功
初始化tsc --init
tsc -w 可以自动编译 ts文件 我们只需要执行对应的js文件就可以了
tsc -ts文件名 将ts文件转义成js文件,ts文件不能直接运行,需要先转义成js


其他工具:

npm i @types/node -D ts-node ts文件名可以直接输出

npm i ts-node -g

npm i nrm -g 管理npm 镜像源

nrm -h 查看命令

nrm ls 查询镜像源 nrm use 镜像源

02-数据类型

  • 布尔类型(boolean)
  • 数字类型(number)
  • 字符串类型(string)
  • 数组类型(array)
  • 对象类型(object)
  • 元组类型(tuple)
  • 枚举类型(enum)
  • 任意类型(any)
  • null 和 undefined
  • void类型
  • never类型

number

1
2
3
4
5
6
7
let num: number = 2         // 普通数字
let notnumber: number = NaN // NaN
let infinityNumber: number = Infinity //无穷大
let decimal: number = 6 //六进制
let hex: number = 0xf00d // 十进制
let binary: number = 0b1010 // 十六进制
let octal: number = 0o744 //八进制

boolean

1
2
let b1: boolean = false
let b2: boolean = true

string

1
let str: string = '1'

null undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let n: null = null
let u: undefined = undefined
n = u
u = n
严格模式
tsconfig.json
// true 开启 false 关闭
"strict": true,
关闭严格模式 nullundefined 可以互相赋值

// null和undefined 是其他数据类型(never类型)的子类型
// 定义为具体类型的变量,不能为undefined,即不能不初始化一个值。但是定义为undefined类型的变量可以不进行初始化
// 所以可以将变量定义为 具体类型变量 或 undefined类型
var num: number | undefined | null // 此时num即可以为空,也可以赋数字的值

// 类似的,一个变量可以定义为多种类型
var a:number | string = 123
a = 'abc'

// undefined和null类型本身的类型用处不是很大

never

表示一中永远无法到达的类型 :type A = string & number // A 就是never类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// never类型: 是其他类型(包括null和undefined)的子类型,代表从不会出现的值,就是说可以把null和undefined赋值给number、string等类型的变量。然而一旦指定了 --strictNullChecks标记,null和undefined就只能赋值给void和它们各自,想给string传入一个null或undefined时需要指定联合类型: string | null | undefined
// 这意味着声明never的变量只能被never类型所赋值
// undefined只能赋值undefined,null只能赋值null
var a:undefined
a = undefined
var b:null
b = null


// never表示从不会出现的值,例如throw一个错误
// never类型是任何类型的子类型,可以赋值给任何类型
// 没有类型是never类型的子类型或赋值给never类型(除了never本身)
// 一般情况下用不到
// 返回never的函数必须存在无法到达的终点:例如抛出异常、死循环
var c:never
c = (() =>{
throw new Error('错误')
})()

function infiniteLoop():never {
while(true){
}
}


// never 类型在联合类型里面会被忽略掉,所以这里的 B 类型为 string | void
type B = string | void | never

type Kun = '唱' | '跳' | 'rap' | '篮球'

function fn(value: Kun) {
switch (value) {
case '唱':
break
case '跳':
break
case 'rap':
break
case '篮球': //如果这里的篮球不加就会报错,因为不能把string赋值给never
break
default:
const error:never = value
break
}
}

void 类型

1
2
3
4
5
6
7
// void类型: 表示没有任何类型,一般用于定义方法的时候表示没有返回值
function run():void{
console.log('run')
}

// 声明一个void类型的变量没有什么作用,因为它只能被赋予undefined和null
let unusable: void = undefined

Symbol类型

Symbol 表示唯一值,每个Symbol 数据都是唯一的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let a1:symbol = Symbol(1)
let a2:symbol = Symbol(1)
console.log(a1,a2) // Symbol(1) Symbol(1)
a1 === a2 a1 == a2 // 都为false


// 那么如何让两个 Symbol 返回true呢?
// for会去全局Symbol里面查看有没有注册过这个key,如果有直接拿来用,不会创建新的,没有的话就创建新的
console.log(Symbol.for('1') === Symbol.for('1')) // true

// 场景
let a1:symbol = Symbol(1)
let a2:symbol = Symbol(1)
let obj = {
name: 1,
[a1]: 111,
[a2]: 222,
name: 80
}
// obj {name:80,[Symbol(1)]:111,[Symbol(1)]:222}
// 怎么取到 a1和a2 的值

for(let key in obj){
console.log(key) // name:1
}
console.log(Object.keys(obj)) // 也读取不到
console.log(Object.getOwnPropertyNames(obj)) // 读取不到
console.log(Object.getOwnPropertySymbols(obj)) // 可以读取到symbol 但是丢失name
console.log(Reflect.ownKeys(obj)) // {name:80,[Symbol(1)]:111,[Symbol(1)]:222}

03-any 和 unknow 的区别

类型级别

  1. 顶级类型 any unknow

  2. Object

  3. Number String Boolean

  4. number string boolean symbol null undefined

  5. 1 ‘张三’ false

  6. never

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// any类型可以赋任意类型的值
var num:any = 123
num = true
num = 'abc'

// unknown 只能赋值给自身 或者是 any
// unknown 没有办法读取对象的任何属性,方法也不能调用
let a: unknown = 1
let b: number = 2
let c: any = 3
b = a // 报错
c = a

let obj: unknown = {
name: '张三',
setname: () => 123
}
console.log(obj.name) // obj类型未知
//unknown 类型比 any 类型更加安全

总结:

  • any类型可以赋给任意类型的值
  • unknown 只能赋值给自身 或者是 any
  • unknown 没有办法读取对象的任何属性,方法也不能调用
  • unknown 类型比 any 类型更加安全

04-Object object {}

Object大写

1
2
3
4
5
6
7
// 包含所有类型的 Object
let a:Object = '123'
let a1:Object = 123
let a2:Object = false
let a3:Object = []
let a4:Object = {}
let a5:Object = ()=> 123

object小写

1
2
3
4
5
6
7
// 不包含原始类型
let a:object = '123' // 错误 原始类型
let a1:object = 123 // 错误 原始类型
let a2:object = false // 错误 原始类型
let a3:object = [] // 正确
let a4:object = {} // 正确
let a5:object = ()=> 123 // 正确

{} 字面量

1
2
3
4
5
6
7
8
9
10
11
// 包含所有类型的 Object
let a:{} = '123'
let a1:{} = 123
let a2:{} = false
let a3:{} = []
let a4:{} = {}
let a5:{} = ()=> 123

let b:{} = {name:'张三'}
// 字面量{} 对象类型 赋值后不能修改
b.age = 25 // 无法添加

05-interface接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

// 被约束的类型 不能多属性 也不能少属性
// interface 同名的会重合,将所有的类型合到一起
interface msg {
name: string
age: number
}
interface msg {
gender: string
}
let a: msg = {
name: '张三',
age: 88,
gender: '男'
}

// 索引签名 当后端接口返回数据过多,我们只需要其中某一些属性
// propName -> 对象的key
interface data {
name: string // 必须有
age: number // 必须有
[propName:string]:any // 索引签名
}
let res:data = {
name: '张三',
age: 88,
a:1, //下面的不参与ts校验
b:2,
c:3
}

// 接口继承
// 可以继承多个 interface a extends b 接口名
interface a extends b {
name: string
}
interface b {
age: number
}
let obj:a = {
name: '张三',
age: 88,
}

// 使用interface 定义函数
interface Fn {
(name:string):number[]
}
const fn:Fn = function(name:string) {
return [1]
}
  • 接口继承
  • 索引签名
  • 接口重合

06-可选参数

1
2
3
4
5
6
7
8
9
interface msg {
name: string
age?: number
}

let a = {
name:'zs',
age:10 //这里的age为可选参数
}

07-readonly

1
2
3
4
5
interface msg {
name: string
age?: number
readonly fn:()=>false // fn 方法只读,不可修改
}

08-数组的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// <> 表示泛型
// 第1种定义数组的方式
var arr:number[] = [12, 5, 8] // 数字数组
// 第2种定义数组的方式
var arr:Array<number> = [12, 5, 8]

//大杂烩数组
let arr:any = [12, "abc","123"]

// 元组类型,属于数组的一种,可以为数组中每一个位置元素指定一个类型
let arr:[number, string, string] = [12, "abc","123"]

// 使用 interface 定义数组
interface obj {
name:string
age?:number
}
const arr:obj = [
{name:'张三',age:28},
{name:'李四'}
]
// 二维数组
var arr:number[][] = [[12], [5], [8]] // 数字数组
var arr:Array<Array<number>> = [[12], [5], [8]]


function (...args:string[]) {
let a:any[] = arguments // 报错因为 arguments 是伪数组很多数组方法都没有
let a:A = arguments
let a:IArguments= arguments // 原理就是 通过interface
}
a('1','2')
interface A {
callee:Function
length:number
[index:number]:any
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 枚举类型
// 枚举类型中间没有等号
enum Flag {success = 1, error = 2}
// 如果定义一个数字枚举,第一个成员初始化为1,其余成员会自动从1开始增长
enum Direction {
Up = 1, // 如果不指定Up初始化值,则 Up的值为0,Down为1
Down, // Up = 1时,Down会自动变成2
Left,
Right
}

// 使用
let s:Flag = Flag.success
console.log(s) // 输出1

// 如果枚举的标识符没有赋值,打印出来的为前一个的值加1。如果第一项没有值,则第一项值默认为0
enum Color {blue, red, yellow = 3, green, 'white'}
console.log(Color.blue) // 0
console.log(Color.red) // 1
console.log(Color.yellow) // 3
console.log(Color.green) // 4
console.log(Color.white) // 5

09-函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 函数定义
function add(a: number,b: number):number {
reutrn a+b
}
console.log(add(1,1)) //2

// 箭头函数

const add = (a: number,b: number):number => a+b
console.log(add(1,2)) // 3

// 函数参数默认值
const add = (a: number = 10,b: number = 20):number => a+b

// 函数可选参数 可选参数不能同时和默认参数一起使用
const add = (a: number = 1,b?: number):number => a+b

// 如何传递一个对象
interface user {
name: number[],
add: (this:obj,num:number) => void //num 表示传递的参数
}
// ts 中可以定义 this 的类型 在js中无法使用 必须是第一个参数定义 this的类型
let obj = {
name: [1,2,3],
add(this:obj,num:number) {
this.user.push(num)
}
}
obj.add(4)
console.log(obj.user) // [ 1, 2, 3, 4 ]

// 函数重载
let arr: number[] = [1, 2, 3]
function findNum(add:number[]):number[] // 如果传入的是一个number的数组那就添加
function findNum(id:number):number[] //如果传入了id就是单个查询
function findNum():number[] //如果什么都不穿就是查询全部
function findNum(ids?: number | number[]): number[] {
if (typeof ids === 'number') {
return arr.filter(item => item == ids)
} else if (Array.isArray(ids)) {
arr.push(...ids)
return arr
} else {
return arr
}
}

console.log(findNum([11]))

9-类型断言 | 联合类型 |交叉类型

当自己知道某个变量的类型时,可以通过类型断言这种方式告诉编译器。类型断言好比其他语言中的类型转换,但是不进行特殊的数据检查和结构,它没有运行时影响,只在编译阶段起作用。

!! 类型强转

!1 -> false ->!false -> true !!1 -> true

1
2
3
4
5
6
7
8
// 联合类型
// let phonw:number | string = 17762682464
// 注意:这里不能使用 ||
let fn = function (type: number | boolean):boolean {
return !!type
}
let result = fn(false)
console.log(result) //false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 交叉类型
interface person {
name:string,
age:number
}
interface Man {
sex:number
}

const fn = (man: person & Man): void => {
console.log(man)
}
fn({
name: '张三',
age: 20,
sex: '男'
}) // { name: '张三', age: 20, sex: '男' }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 类型断言
let fn = (num: number | string) => {
console.log((num as string).length)
}
fn('123')

// 类型断言也可以这样写
interface A {
run: string
}

interface B {
bulid: string
}

let o = (type:A | B):void => {
console.log((<A>type).run)

}
o({run:'1'})

window.a =123 // 报错 类型“Window & typeof globalThis”上不存在属性“a”
(window as any).a = 123 // any 类型上面可以访问任何属性和方法

const fn = (type:any):void {
return type as boolean
}
console.log(fn(1)) // 1 类型断言并不会欺骗 ts

10-内置对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let num:Number = new Number(1)
let data:Date = new Date()
let reg:RegExp = new RegExp(/\w/)
let error:Error = new Error('错误')
let xhr:XMLHttpRequest = new XMLHttpRequest()

//HTML元素 Element HTMLElement
let div:HTMLDivElement = document.querySelector('div')

// NodeList(可遍历) dom 类型可以使用 forEach遍历
let div:NodeList = document.querySelectorAll('div')
//当类型不确定可以
let div:NodeListof<HTMLDivElement | HTMLElement> = document.querySelectorAll('div')

let local:Storage = localStorage
let lo:Location = location
let cookie:string = doucment.cookie
let promise:Promise<number> = new Promise((r)=>r(1))
promise.then(res=>{
res. //可以使用对应的数字方法
})
let promise:Promise<string> = new Promise((r)=>r(1))
promise.then(res=>{
res. //可以使用对应的字符串方法
})

11-类型推论 | 类型别名

类型推论

1
2
3
4
5
6
7
let str = '张三'  	// 鼠标滑到str上面显示string类型
str = 345 // 赋值其他类型,报错
let arr = [1] // 也可以推论其他类型 number类型的数组

let a // 如果默认没有赋值和类型那么就是any类型
a = 123
a = null

类型别名

1
2
3
4
5
6
7
8
9
10
// type 可以定义任何类型
type fn = () => void // const f: fn = () => { }
type o = {name:'张三'}
type s = string
type narr = number:[]

// extends 在ts里面表示包含的意思
// 左边的值 会作为右边类型的子类型
type num = 1 extends number ? 10 // 返回结果为1
// 只有当number为 never 的时候为0 因为never的级别最低

12-生成器 | 迭代器

生成器

1
2
3
4
5
6
7
8
9
10
function* gen() {
// yield 同步和异步代码可以修饰
yield Promise.resolve(1)
yield 2
}
const man = gen()
// value 表示值(yield右边的值),done 为false表示可以继续执行,true不能执行
console.log(man.next()) // {value:Promise {1},done:false}
console.log(man.next()) // {value:2,done:false}
console.log(man.next()) // {value:undefined,done:true}

迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
set map arr NodeList(dom节点) 等这些都是 有迭代器的 可以通过 [Symbol.iterator]()获取
const set:Set<number> = new Set([1,2,3])
const each = (value:any) => {
let It:any = value[Symbol.iterator]()
let next:any = {done:false}
while(!next.done) {
next = It.next()
if(!next.done) {
console.log(next.value)
}
}
}
each(set) // 1,2,3
// 迭代器的语法糖 for of 对象不可用
for(let value of set) {
console.log(value) // 效果是一样的
}
// 解构的底层原理 iterator
// 对象支持 for of
let obj = {
max:5,
current:0,
[Symbol.iterator](){
return {
max:this.max,
current:this.current
next() {
if(this.current == this.max) {
return {
value:undefined
done:true
}
}else {
return {
value:this.current++
done:false
}
}
}
}
}
}
let x = [...obj] let y = {...obj}
console.log(x) // [0,1,2,3,4]
console.log(y) // {max:5,current:0,[Symbol.iterator]}
// 数组解构底层调用interator 对象不是

13-泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function A(a:number, b:number):Array<number>{return [a,b]}
function B(a:number, b:number):Array<number>{return [a,b]}

function fn<T>(a:T, b:T):Array<T> { return [a, b] }
fn(1,2) // function fn5<number>(a: number, b: number): number[]
fn('1','2') // function fn5<string>(a: string, b: string): string[]

type A<T> = string | number | T
type a:A<null> = null

iterface Data<T> = {
msg:T,
}
let data:Data<number> = {
msg:1
}

function add<K, T>(a: K, b: T): Array<K | T> {
return [a, b]

}
add(1, false)

// 封装axios
const axios = {
get<T>(url:string):Promise{
return new Promise((resolve,reject)=>{
let xhr:XMLHttpRequest = new XMLHttpRequest()
xhr.open('GET',url)
xhr.onreadystatechage = () => {
if(xhr.readyState === 4 && xhr.status == 200){
resolve(JSON.parse(xhr.responseText))
}
}
xhr.send(null)
})
}
}
axios.get('./data.json').then(res=> {
console.log(res) // {messge:"success",code:200}
})
// data.json
{
"message":"success",
"code":200
}

14-泛型约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 在类型后面在跟一个 extends 在跟一个约束的类型
function add<T extends number> (a:T,b:T) {
return a + b
}
console.log(add(2,3)) // 5

interface len {
length:number
}
function fn<T extends len>(a:T) {
console.log(a.length)
}
fn('111')
fn([1,2,3])
fn(false) // 报错 没有length属性
fn(111) // 报错 没有length属性


let obj = {
name: '张三',
sex: '男'
}
type key = keyof typeof obj
function ob<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
ob(obj, 'name')

interface Data {
name:string,
age:number,
sex:string
}
// 实现一个功能将 接口 Data里面的类型全部变成可选参数 不通过直接加?的方式
// for in for(let key in obj)
type Options<T extends object> = {
readonly [key in keyof T]?: T[key]
}

type c = Options<Data>

ts.config 配置文件

ts 命名空间污染

如果模块中没有带有 import 和 export声明 那么它的内容就是全局可见的(因此模块也是全局可见的)

1
2
3
4
5
6
// index.ts
const a = 1
// index2.ts
const a = 2 // 提示变量 a已经被声明了
// 解决方法
export const a = 2

命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// index.ts
namespace A {
export const a = 1
}
// index2.ts
namespace B {
export const a = 2
}
// 也可以做到互不干扰
// 命名空间会被编译成一个对象 console.log(A.a) // 1

// 嵌套命名空间
namespace A {
export namespace C {
export const D = 5
}
}
console.log(A.C.D)

// 抽离命名空间
// 文件一
export namespace B {
export const a = 2
}
// 文件二
import {B} from './B'
console.log(A.C.D,B)

// 命名空间简化
import AA = A.C
console.log(AA.D)

多个命名空间

1
2
3
4
5
6
7
8
9
10
11
12
namespace A {
export const a = 1
}
namespace A {
export const b = 2
}

// 和 interface 一样,相同的会合并
namespace A {
export const a = 1
export const b = 2
}

三斜线指令

1

声明文件

在ts项目中要使用第三方包就必须下载对应的声明文件,如果没有就会报错

  • 一种是直接去社区找对应的包的ts声明文件 @types/包名
  • 一种是自己手动写一个声明文件 规范 包名.d.ts

Mixins混入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 混入
interface Name {
name: string
}

interface Age {
age: number
}

interface Sex {
sex: string
}

let a: Name = { name: '张三' }
let b: Age = { age: 23 }
let c: Sex = { sex: '男' }

let MixisObj = Object.assign(a, b, c)