Bootstrap

【TypeScript】TS入门到实战(详解)

目录

第一章 前言

1.1 介绍TypeScript

1.2 TypeScript相比Js的优势

1.3 使用TypeScript的准备工作

第二章 TypeScript的数据类型

2.1 TypeScript的常用类型

2.1.1 概述

2.1.2 TS使用JS基本数据类型

2.1.2.1 number

2.1.2.2 string

2.1.3.3 boolean

2.1.2.4 null

2.1.2.5 undefined

2.1.2.6 symbol

2.1.2.7 总结

2.1.3 TS使用JS引用数据类型

2.1.3.1 数组(array)

2.1.3.2 函数(function)

2.1.3.3 对象(object)

2.1.4 联合数据类型 

2.1.5 类型别名(type)

2.1.6 接口(interface)

2.1.6.1 类型别名与接口的区别

2.1.6.2  接口继承(extends)

2.1.7 元组(tuple)

2.1.8 类型推论

2.1.9 类型断言(as)

2.1.10 字面量类型(const)

2.1.11 枚举(enum)

2.1.12 any类型

2.1.13 typeof


第一章 前言

1.1 介绍TypeScript

  • TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
  • 从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JS 属于动态类型的编程语言。 静态类型:编译期做类型检查; 动态类型:执行期做类型检查。 代码编译和代码执行的顺序:先编译再执行。​​​​​​
  • 由于TS,在编译时就开始做类型检查了,所以TS 可以提前到在编写代码的同时就发现代码中的错误,减少找 Bug、改 Bug 时间

1.2 TypeScript相比Js的优势

  • 更早(写代码的同时)发现错误,减少找 Bug、改 Bug 时间,提升开发效率。
  • 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
  • 强大的类型系统提升了代码的可维护性,使得重构代码更加容易
  • 支持最新的 ECMAScript 语法,优先体验最新的语法,让你走在前端技术的最前沿。
  • TS 类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。

1.3 使用TypeScript的准备工作

  • 安装ts 的工具包,原因:(浏览器与node只认识js代码,不认识ts代码,安装ts的工具包目的是将ts转为js)
npm i -g typescript

利用tsc -v查看是否安装成功

tsc -v

 

使用顺序 :

  1. 例如:创建 hello.ts 文件(注意:TS 文件的后缀名为 .ts
  2. 将 TS 编译为 JS:在终端中输入命令,tsc hello.ts(此时,在同级目录中会出现一个同名的 JS 文件)
  3. 执行 JS 代码:在终端中输入命令,node hello.js
  • 使用 ts-node 包,直接在 Node.js 中执行 ts 代码
npm i -g ts-node

利用ts-node -v查看是否安装成功

ts-node -v

使用顺序:直接ts-node hello.ts

第二章 TypeScript的数据类型

2.1 TypeScript的常用类型

2.1.1 概述

  • 可以将 TS 中的常用基础类型细分为两类:JS 已有类型 与 TS 新增类型。
  • JS已有数据类型:基本数据类型与引用数据类型
  1. 基本数据类型:number、string、boolean、null、undefined、symbol
  2. 引用数据类型:object(包括:数据array、函数function、对象等)
  • TS新增类型
  1. 自定义类型(类型别名)、联合类型、接口、元组、自变量类型、枚举、void、any等等

2.1.2 TS使用JS基本数据类型

2.1.2.1 number
let number: number = 23
2.1.2.2 string
let myName: string = '关注ve'
2.1.3.3 boolean
let isYouth: boolean = true
2.1.2.4 null
let a: null = null
2.1.2.5 undefined
let b: undefined = undefined
2.1.2.6 symbol
let s: symbol = Symbol()
2.1.2.7 总结
  • 写法:  变量 :数据类型 = 类型值
  • 通过数据类型为变量进行约束,从而只能给变量赋值该类型的值
  • 代码中查看数据的数据类型:(鼠标悬浮在查看的变量上)

  • 当赋值其他类型是报错,例如

2.1.3 TS使用JS引用数据类型

2.1.3.1 数组(array)
  • 写法一:变量:数据类型[] = ['该数据类型的值1','该数据类型值2'……]——(推荐)
const numbers: number[] = [1,2,4,5] // 该写法是一个纯数字数组数据类型,数组内只包含数字类型
const strs: string[] = ['1','2','3'] // 数组内只包含字符串类型
  • 写法二:变量:Array<数据类型> = ['该数据类型的值1','该数据类型值2'……]
const numbers2: Array<number> = [1,2,4,5]
const strs2: Array<string> = ['1','2','3']
  • 解释:针对于以上两种方法,只能定义一个数组,然后数组的值只能是纯规定数据类型的值,数组中不能又其他类型的值
  • 需求:如果一个数组中能有nuber、 string、boolean等多个数据类型在同一个数组中
  • 解决:联合数据类型: 变量: (数据类型)[] = [字面量,……](后续会对该类型具体解释)
const arr: (number | string | null | boolean)[] = [1,'a',false, null] 
// 该写法将|理解成或者,表示arr变量是一个数组,数组里面的值可以有数据类型、字符串类型、null、boolean
2.1.3.2 函数(function)
  • 说到函数,我们可以知道有参数和返回值两种
  •  写法一:单独指定参数、返回值的类型
  1.  普通函数:function 函数名 (参数1: 参数类型, 参数2: 参数类型…): 函数返回数据类型 {}
  2. 箭头函数:const 函数名 = (参数1: 参数类型, 参数2: 参数类型…): 函数返回数据类型 => {}
function add (number1:number, number2: number): number { // 普通函数
    return number1 + number2
}
console.log(add(1,2))
const addFn = (number1:number, number2: string): string => { // 箭头函数
    return number1 + number2
}
console.log(addFn(2,'fv'))
  • 写法二:同时指定参数、返回值的类型(这种方法适用于箭头函数)
  1. const 函数名: (参数1: 参数类型,参数2: 参数类型…) => 函数返回数据类型 = (参数1,参数2) => {}

// 只适用于函数表达式,例子:
const addFn: (number1:number, number2: string) => string = (number1,number2) => {
    return number1 + number2
}
  • 如果函数没有返回值,那么,函数返回值类型为:void

function returnvoid(number:number): void {
    console.log("函数返回空值", number)
}
returnvoid(2222)
  • 使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。

  1. 可选参数: 在可传可不传的参数名称后面添加 ?(问号)

  2. 注意:必填参数必须在可选参数之前

function mySlice(start:number,end?:number):void {
    console.log("开始:" + start + '结束' + end )
}
mySlice(1)
mySlice(1,6)
2.1.3.3 对象(object)
  • JS中的对象是由属性和方法构成的,而TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
  1. 直接使用{}来描述对象结构。属性采用属性名: 类型的形式方法采用方法名(): 返回值类型的形式
  2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如: greet(name: string): void )
  3. 在一行代码中指定对象的多个属性类型时,使用; / ,( 分号或者逗号) 来分隔

        - 如果一行代码只指定一个属性类型( 通过换行来分隔多个属性类型),可以去掉;(分号)。

        - 方法的类型也可以使用箭头函数形式(比如 :{sayHi: () => void1}

// 写法例子
let person: {
    name: string,
    age: number, 
    say:() => void,
    greet(name:string):void
} = {
    name: '李四',
    age: 18,
    // 注意:我们这里最好是跟上面描述的写法写,便于阅读,当然这么写也不会错
    // say(){
    //     console.log("说hello")
    // },
    // greet:(name) => {
    //     console.log("和" + name + "说hello")
    // }
    say:() => {
        console.log("说hello")
    },
    greet(name) {
        console.log("和" + name + "说hello")
    }
}
person.say()
person.greet('zs')
console.log(person.name,person.age)

// 写在一行
// let person: {name: string, age: number, say(name:string):void} = {
//     name: '李四',
//     age: 18,
//     say(name) {
//         console.log("和" + name + "说hello")
//     }
// }

2.1.4 联合数据类型 

  • 联合数据类型:由两个或者多个其他类型组成的类型,表示可以是这些类型中的任意一种

  • 利用 | 连接多个数据类型,| 可理解或者

  • 写法:变量: (数据类型1,数据类型2…)[] = [字面量,……]

// 该写法将|理解成或者,表示arr变量是一个数组,数组里面的值可以有数据类型、字符串类型、null、boolean
const arr: (number | string | null | boolean)[] = [1,'a',false, null] 
//该写法表示data可以是number类型、纯字符串数组、存在null/boolean的数组
const data: number | string[] | (null | boolean)[] =  ['111']

2.1.5 类型别名(type)

  • 类型别名( 自定义类型 ) : 为任意类型起别名

  • 使用场景: 当同一类型(复杂 )被多次使用时,可以通过类型别名,简化该类型的使用。

  • 使用方法:

  1. 使用 type 关键字来创建类型别名

  2. 类型别名(比如:a,b…但是最好语义化一些),可以是任意合法的变量名称

  3. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可

type CustomArray = (number | string)[]
const array1: CustomArray = [1,2,'a','v']
const array2: CustomArray = [2,'a','b']

type a = (number | string)[] | number
const array3: a = [1,2,'a','v']
const array4: a = [2,'a','b']
const number: a = 20

2.1.6 接口(interface)

  • 当一个对象类型被多次使用时,一般会使用接口 (interface )来描述对象的类型,达到复用的目的

  • 使用方法:

  1. 使用 interface 关键字来声明接口。

  2. 接口名称(比如:a,b…但是最好语义化一些 ),可以是任意合法的变量名称

  3. 声明接口后,直接使用接口名称作为变量的类型

// 使用接口 -> 这里是两个对象使用的相同键值对以及类型
interface IPerson {
    name: string
    age: number, 
    say:() => void,
    greet(name:string):void
}
let person1: IPerson = {
    name: '张三',
    age: 18,
    say(){
        console.log("说hello")
    },
    greet:(name) => {
        console.log("和" + name + "说hello")
    }
}
let person2: IPerson = {
    name: '李四',
    age: 18,
    say(){
        console.log("说hello")
    },
    greet:(name) => {
        console.log("和" + name + "说hello")
    }
}
// 多个对象有共同的键值对
// let person1: {
//     name: string
//     age: number, 
//     say:() => void,
//     greet(name:string):void
// } = {
//     name: '李四',
//     age: 18,
//     say(){
//         console.log("说hello")
//     },
//     greet:(name) => {
//         console.log("和" + name + "说hello")
//     }
// }

// let person2: {
//     name: string
//     age: number, 
//     sex: string,
//     say:() => void,
//     greet(name:string):void
// } = {
//     name: '李四',
//     age: 18,
//     sex: '男',
//     say(){
//         console.log("说hello")
//     },
//     greet:(name) => {
//         console.log("和" + name + "说hello")
//     }
// }
2.1.6.1 类型别名与接口的区别
  • 相同点:都可以给对象指定类型

  • 不同点:

  1. 接口(interface)只能给对象指定类型,而类型别名(type)不仅可以给对象指定类型也可以给为任意类型指定别名

  2. 写法上接口(interface)类似于声明函数,函数中是不同属性/方法的类型描述,而类型别名(type)类似于赋值类型

interface IPerson {
    name: string
    age: number, 
    say:() => void,
    greet(name:string):void
}

type IPerson = {
    name: string
    age: number, 
    say:() => void,
    greet(name:string):void
}

type newarr = number | string
2.1.6.2  接口继承(extends)
  • 两个接口之间有相同的属性或方法,可以 将公共的属性或方法抽离出来,通过继承来实现复用
interface Point2D {
  x: number
  y: number
}
// interface Point3D { x: number; y: number; z: number } // 其中x, y是与point2D的x, y相同

// 使用 继承 实现复用:
interface Point3D extends Point2D {
  z: number
}

let p3: Point3D = {
  x: 1,
  y: 1,
  z: 0
}

2.1.7 元组(tuple)

  • 场景:在一些特殊情况下,例如地图坐标:使用经纬度坐标来标记位置信息。
  • 1、如果我们使用如下方法定义一个数组:
let position: number[]

我们从之前的方法可以知道,这其实定义的是一个纯数字类型的数组,长度任意长

let position: number[] = [39, 114, 1, ,3, 4]

但是,我们实际需要得到的是坐标信息,只需要有经纬度的值就可以了,如果使用以上方法,后面的值就没有意义了

  • 2、使用元组类型
let position: [number, string] = [39, '114']

 -- 元组:确切的控制了这个数据有多少个元素,以及元素的类型;

-- 该例子是定义了一个变量有两个元素,第一个元素是number数据类型,第二个是string类型;

-- 如果赋值多了会报错,不对应赋值也会报错,如下所示

2.1.8 类型推论

  • ts中在某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型
  • 常见场景:1.声明变量并初始化时,2.决定函数返回值时

  • 但是如果声明变量没有立即初始化,则必须添加类型注解

否则:

2.1.9 类型断言(as)

  • 指定更具体的类型
  1. as关键字
  2. 关键字as后面是一个更加具体的类型
const aLink = document.getElementById('link') as HTMLAnchorElement
  • 使用<>语法,这种语法形式不常用:(与react语法冲突,不常用
const aLink = <HTMLAnchorElement>document.getElementById('link')
  •  获取具体元素的方法:在浏览器控制台,先选中元素,此时元素后面会被添加$0,通过 console.dir() 打印该 DOM 元素,在属性列表的最后面,即可看到该元素的类型。

2.1.10 字面量类型(const)

  • 初体验,代码如下:
let str1 = 'hello'
const str2 = 'hello'

  1. 根据类型推论我们可以发现,str1与str2的类型分别是string与 'hello'
  2. str1 是一个变量(let),它的值可以是任意字符串;而str2 是一个常量(const),它的值不能变化只能是 'hello'
  • 除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用

  • 使用场景:表示一组明确的可选值列表一般搭配联合类型一起使用,例如:

2.1.11 枚举(enum)

  • 类似字面量类型+联合类型组合的功能用来表示一组明确的可选值
  • 初体验,代码如下:
enum Direction { Up,Down,Right,Left }

function changeDirection(direction: Direction) {
    return direction
}

console.log(changeDirection(Direction.Up))

-- 输出结果: 

-- 扩展1:

enum Direction { Up=6,Down,Right,Left }

-- 总结:

  1. 使用enum关键字定义枚举
  2. 约定枚举名称、枚举中的值以大写字母开头
  3. 枚举中的多个值之间通过 ,(逗号)分隔
  4. 定义好枚举后,形参直接使用枚举名称作为类型注解
  5. 实参的值可以是枚举 Direction 成员的任意一个,接通过点(.)语法访问枚举的成员
  6. 从输出结果为0,我们可以知道表示枚举的值是会携带默认值的,从 0 开始自增的数值
  7. 通过扩展1能知道数字的起始值可以自定义,并且自增只针对数字类型
  8. 枚举中的成员也可以自定义初始化值
  • 字符串枚举 :枚举成员的值是字符串
enum Direction { Up = 'up', Down = 'down', Right = 'right', Left = 'left'}

function changeDirection(direction: Direction) {
    return direction
}

console.log(changeDirection(Direction.Up))

  1. 注意: 字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值
  • 转换为js文件

  1.  由于其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值枚举不仅作为类型,还提供值);所有其他类型会在编译成js是自动移除,但是枚举会被编译成js代码;
  2. 一般情况下, 推荐使用字面量类型+联合类型组合的方式 ,相比枚举更加直观、简洁、高效。

2.1.12 any类型

  • 不推荐使用,当值为any类型时,可以对改值进行任意操作,并且不会有代码提示;也不会有报错,失去了ts的意义

  • 其他隐式具有any类型情况:1 声明变量不提供类型也不提供默认值  2 函数参数不加类型

2.1.13 typeof

  • 初使用:
let number1: number = 10
let number2 = typeof number1
console.log(number2)
let obj1 = {x: 1, y: 2}
let obj2 = typeof(obj1)
console.log(obj2)
function dot(point: { x: number, y: number }) {
    return point
}
console.log(dot(obj1))
function dot2(point: typeof obj1) {
    return point
}
console.log(dot2(obj1))

  1. 使用场景:根据已有变量的值,获取该值的类型,来简化类型书写
  2. 如果直接使用typeof,获取的就是获取变量的类型,如上前两个输出
  3. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码),如上最后一个输出。
;