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

339 阅读13分钟

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

第一章 前言

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

12ddb7f743c7a05782c9f05d55205488.png

使用顺序fee682392ac88338ef423337d457db51.png

  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 c8295f8c1db14f9888bcff496f158028.png 使用顺序:直接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 总结
  • 写法: 变量 :数据类型 = 类型值

  • 通过数据类型为变量进行约束,从而只能给变量赋值该类型的值

  • 代码中查看数据的数据类型:(鼠标悬浮在查看的变量上) 46887024f89f119a9459a82f94bac317.png

  • 当赋值其他类型是报错,例如 25c0a5242eaba8ee7f2a5d50e6111947.png

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']
  • 解释:针对于以上两种方法,只能定义一个数组,然后数组的值只能是纯规定数据类型的值,数组中不能有其他类型的值
  • 需求:如果一个数组中能有number、 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)  //函数返回空值2222
  • 使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。

    1. 可选参数: 在可传可不传的参数名称后面添加 ?(问号)
    2. 注意:必填参数必须在可选参数之前
function mySlice(start:number,end?:number):void {
    console.log("开始:" + start + '结束' + end )
}
mySlice(1) //开始1结束undefind
mySlice(1,6) //开始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类型;

    • 如果赋值多了会报错,不对应赋值也会报错,如下所示 c3aded5d92a4d9ad08d4a7860fde2963.png 2130019510eca5dae726ad027c0f2a2a.png

2.1.8 类型推论

  • ts中在某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型
  • 常见场景:1.声明变量并初始化时,2.决定函数返回值时 a0b95079cdc7878b607835bfba834661.png 41c1d5b4b3179c436630c914785b86ed.png
  • 但是如果声明变量没有立即初始化,则必须添加类型注解 e6743fb1ffb4c2af4940455f90316cc8.png 否则: 4379cc96b736cb238922efd1d62959a2.png

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 元素,在属性列表的最后面,即可看到该元素的类型。 f9752abff4a6a0eeb732ba085f5e16a9.png

2.1.10 字面量类型(const)

  • 初体验,代码如下:

    let str1 = 'hello' const str2 = 'hello'

    5eed6e63053aa676cafca732eaf1c6a0.png 4fdfe8d9cb7320d0601f814489e050f2.png

    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))
  • 输出结果: 1932fe5d0a4fae4d95b2754cac59d543.png 219483c3f9b970ad28d188b89395d87a.png

  • 扩展1:

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

98f709bc283e178ec364cd7ea5f89c8a.png b2c92339d545ca726b89de0b19e052d9.png ee32b13c47eb36e079af0ddf1bc7575f.png d6f24e9996808e55b0ce07db6dbeb3c1.png

  • 总结:

    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))

bf7fd730817436531e889104979e0446.png

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

    9ad1fcc9dd43058af438baf9d820b23a.png 7ad5622fdd0f940d7eb7bd6670c2a283.png

    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))

52c0ee0027ec53f02e9278c004ea30c8.png

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

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

作者:❆VE❆
原文链接:blog.csdn.net/qq_45796592…