1. TypeScript 简介

image
  1. TypeScript由**微软开发,是基于JavaScript的一个扩展语言**。
  2. TypeScript包含了JavaScript的所有内容,即:TypeScriptJavaScript的**超集**。
  3. TypeScript增加了:静态类型检查、接口、 泛型等很多**现代开发特性,更适合大型项目**的开发。
  4. TypeScript需要**编译**为JavaScript,然后交给浏览器或其他JavaScript运行环境执行。

2.为何需要 TypeScript

2.1 今非昔比的 JavaScript(了解)

  • JavaScript 当年诞生时的定位是浏览器**脚本语言,用于在网页中嵌入简单的逻辑**,且代码量很少。
  • 随着时间的推移,JavaScript 变得越来越流行,如今的 JavaScript 已经可以全栈编程
  • 现如今的 JavaScript <应用场景比当年丰富的多,代码量也比当年大很多,随便一个 JavaScript 项目的代码量,可以轻松的达到几万行,甚至十几万行!
  • 然而当年 JavaScript出生简陋, 没考虑到如今的应用场景和代码量,逐渐就出现了很多困扰

2.2 JavaScript 中的困扰

  • 1.不清楚的数据类型
1
2
let welcome = 'hello'
welcome() // 此行报错:TypeError: welcome is not a function
  • 2.有逻辑漏洞
1
2
3
4
5
6
7
const str = Date.now() % 2 ? '奇数' : '偶数'
if (str !== '奇数') {
alert('hello')
} else if (str === '偶数') {
alert('world')
}
--- 逻辑异常 else if中的数据永远不能执行
  • 3.访问不存在的属性
1
2
3
const obj = { width: 10, height: 15 };
const area = obj.width * obj.heigth;
--- 字母错误 也不会提示
  • 4.不会检查函数是否有效
1
2
3
const message = 'hello,world'
message.toUperCase()
--- toUperCase()不是一个函数 缺少字母p toUpperCase()

2.3 静态类型检查

  • 在代码运行前进行检查,发现代码的错误或不合理之处,减小运行时出现异常的几率,此种检查叫『静态类型检查』,TypeScript 和核心就是『静态类型检查』,简言之就是**把运行时的错误前置**。
  • 同样的功能,TypeScript 的代码量要**大于** JavaScript,但由于 TypeScript 的代码结构更加清晰,在后期代码的维护中 TypeScript 却**胜于 **JavaScript。

3. 创建TS项目

  • 基于vite的方式创建TS项目
1
npm create vite
  • 输入项目名称 xxxx 选择vue框架 选择typeScript

image-20240922105355156

  • 进入项目下载依赖包
1
2
cd xxxxx项目名称
npm install
  • 运行项目
1
npm run dev
  • 编辑 tsconfig.json 可以修改ts默认配置
1
"noEmitOnError": true

image-20240922110357394

4. 类型声明

4.1 基本类型使用

  • 创建xxxx.ts文件
1
2
3
4
5
6
7
8
9
10
11
12
let a: string 	//变量a只能存储字符串
let b: number //变量b只能存储数值
let c: boolean //变量c只能存储布尔值

a = 'hello'
a = 100 //警告:不能将类型“number”分配给类型“string”

b = 666
b = '你好'//警告:不能将类型“string”分配给类型“number”

c = true
c = 666 //警告:不能将类型“number”分配给类型“boolean”

4.2 字面量赋值(了解)

1
2
3
4
5
let a: '你好'	//a的值只能为字符串“你好”
let b: 100 //b的值只能为数字100

a = '欢迎'//警告:不能将类型“"欢迎"”分配给类型“"你好"”
b = 200 //警告:不能将类型“200”分配给类型“100”

4.3 类型推断

TS会根据我们的代码,进行类型推导,例如下面代码中的变量d,只能存储数字

1
2
let d = -99 //TypeScript会推断出变量d的类型是数字
d = false //警告:不能将类型“boolean”分配给类型“number”

但要注意,类型推断不是万能的,面对复杂类型时推断容易出问题,所以尽量还是明确的编写类型声明!

5 数据类型介绍

5.1 JavaScript中的数据类型

  • string 字符串
  • number 数值型
  • boolean 布尔类型
  • null
  • undefined 未定义
  • bigint 长整型
  • symbol Symbol 是 ES6 新增的一种原始数据类型,它的字面意思是:符号、标记。代表独一无二的值 。
  • object :其中object包含:Array、Function、Date、Error等

5.2 TypeScript中的数据类型

  • 包含上述8种数据类型
  • 包括6种新数据类型
    • any 任意数据类型
    • unknown 未知类型
    • never 不能有值
    • void 函数不能返回任何值
    • tuple 元组: 特殊数组类型
    • enum 枚举类型
  • 2种自定义类型
    • type 定义任意类型
    • interface 定义接口

在JavaScript中的这些内置构造函数:Number、String、Boolean,用于创建对应的包装对象, 在日常开发时很少使用,在TypeScript中也是同理,所以在TypeScript中进行类型声明时,通常都是用小写的number、string、boolean

6. 数据类型解析

6.1 any

any的含义是:任意类型,一旦将变量类型限制为any,那就意味着**放弃了**对该变量的类型检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// any 可以为任意类型
let a: any

a = 100

a = "你好"

a = null

a = undefined

a = false


// 没有明确的表示b的类型是any,但TS主动推断出来b是any —— 隐式的any

let b

b = 100

b = "str"

b = true

注意事项:

1
2
3
4
5
6
7
8
9
10
//any 可以赋值给任意类型
let aa: any

aa = 100

let bb: string

// 对原有数据造成破坏 代码不会报错
bb = aa

6.2 unknown

1.unknown的含义是:未知类型,适用于:起初不确定数据的具体类型,要后期才能确定

2.unknown会强制开发者在使用之前进行类型检查,从而提供更强的类型安全性。

1
2
3
4
5
6
7
8
9
10
let a: unknown

a = 99
a = '1123123'

a = null
let x: number

//不能将unknown 赋值给 x
x = a

6.3 never

never的含义是:任何值都不是,即:不能有值,例如undefinednull''0都不行!

  • 几乎不用never去直接限制变量,因为没有意义,例如:
1
2
3
4
5
6
7
8
let a: never

//不能赋值任何数据
a = 100

a = null

a = undefined
  • never也可用于限制函数的返回值
1
2
3
4
// 限制throwError函数不需要有任何返回值,任何值都不行,像undeifned、null都不行
function throwError(str: string): never {
throw new Error('程序异常退出:' + str)
}
  • never一般是TypeScript主动推断出来的,例如:
1
2
3
4
5
6
7
8
9
10
// 指定a的类型为string
let a: string
// 给a设置一个值
a = 'hello'

if (typeof a === 'string') {
console.log(a.toUpperCase())
} else {
console.log(a) // TypeScript会推断出此处的a是never,因为没有任何一个值符合此处的逻辑
}

6.4 void

void的含义是空,即:函数不返回任何值,调用者也不应依赖其返回值进行**任何操作**!

1
2
3
4
5
6
7
8
9
10
11
12
13
function add():void {
//不能有任何的返回值
}

function add2():void {
//不能有任何的返回值
return
}

function add3():void {
//不能有任何的返回值
return undefined
}

如果一个函数返回类型为void,那么:

  1. 从语法上讲:函数是可以返回undefined的,至于显式返回,还是隐式返回,这无所谓!
  2. 从语义上讲:函数调用者不应关心函数返回的值,也不应依赖返回值进行任何操作!即使我们知道它返回了undefined

6.5 object 对象类型

6.5.1 object用法

object(小写)的含义是:所有非原始类型,可存储:对象、函数、数组等,由于限制的范围比较宽泛,在实际开发中使用的相对较少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let a: object

//1.为a赋值对象
a = {}
a = {name:'张三'}
a = [1,3,5,7,9]
a = function(){}
a = new String('123')

//2.赋值基本类型会报错
let b: object

b = 123

b = "123"

b = null

b = undefined

6.5.2 声明对象类型

1
2
3
4
5
6
7
8
9
10
11
12
13
let person1: {name: string, age: number}
let person2: {name: string; age: number}

//可选参数类型
let person3: {name: string, age?: number}

person1 = {name: "张三", age: 30}

person2 = {name: "李四", age:40}

person3 = {name: "王五"}

person3 = {name: "赵六", age: 50}

6.5.3 索引签名

索引签名: 允许定义对象可以具有任意数量的属性,这些属性的类型可变的 常用于:描述类型不确定的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
// 限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性
let person: {
name: string
age?: number
[key: string]: any // 索引签名,完全可以不用key这个单词,换成其他的也可以
}

// 赋值合法
person = {
name:'张三',
age:18,
gender:'男'
}

6.5.4 声明函数类型

1
2
3
4
5
export let sum: (num1:number, num2: number) => number

sum = (x,y) => {
return x+y
}

备注:

  • TypeScript 中的=>在函数类型声明时表示函数类型,描述其参数类型返回类型
  • JavaScript 中的=>是一种定义函数的语法,是具体的函数实现。

6.5.5 声明数组类型

1
2
let array1: string[]  = ["a","b","c","d"]
let array2: Array<string> = ["a","b","c","d"] //使用泛型定义数据

6.6 tuple

元组(Tuple)是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的可以不同。元组用于精确描述一组值的类型,?表示可选元素。

1
2
3
4
5
6
7
8
// 第一个元素必须是 string 类型,第二个元素必须是 number 类型。
let arr1: [string,number] = ["张三", 200]

// 第一个元素必须是 string 类型,第二个元素是可选的,如果存在,必须是 number 类型。
let arr2: [string,number?] = ["张三"]

// 第一个元素必须是 number 类型,后面的元素可以是任意数量的 string 类型
let arr3: [number,...string[]] = [100,"李四","王五","赵六"]

6.7 enum类型

枚举(enum)可以定义一组命名常量,它能增强代码的可读性,也让代码更好维护

6.7.1 枚举类型讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义一个枚举类型
export enum Result {
SUCCESS,
Fail
}

export let add = function(msg: Result){
if(msg === Result.SUCCESS){
console.log("业务调用成功")
}else if(msg === Result.Fail){
console.log("业务调用失败")
}else{
console.log("参数异常")
}
}

6.7.2 字符串枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//定义一个枚举类型
export enum Result {
SUCCESS = "成功",
Fail = "失败"
}

export let add = function(msg: Result){
if(msg === Result.SUCCESS){
console.log(Result.SUCCESS) //输出 成功
console.log("业务调用成功")
}else if(msg === Result.Fail){
console.log("业务调用失败")
console.log(Result.Fail) //输出 失败
}else{
console.log("参数异常")
}
}

6.7.3 常量枚举

官方描述:常量枚举是一种特殊枚举类型,它使用const关键字定义,在编译时会被内联避免生成一些额外的代码。

何为编译时内联?

所谓“内联”其实就是 TypeScript 在编译时,会将枚举成员引用替换为它们的实际值,而不是生成额外的枚举对象。这可以减少生成的 JavaScript 代码量,并提高运行时性能。

案例1: 使用普通枚举的 TypeScript 代码如下:

1
2
3
4
5
6
7
enum Directions {
Up,
Down,
Left,
Right
}
let x = Directions.Up;
  • 编译之后 内部会有大量的对象引用
1
2
3
4
5
6
7
8
9
10
"use strict";
var Directions;
(function (Directions) {
Directions[Directions["Up"] = 0] = "Up";
Directions[Directions["Down"] = 1] = "Down";
Directions[Directions["Left"] = 2] = "Left";
Directions[Directions["Right"] = 3] = "Right";
})(Directions || (Directions = {}));

let x = Directions.Up;

常量枚举

  • 使用const 变量命名
1
2
3
4
5
6
7
8
const enum Directions {
Up,
Down,
Left,
Right
}

let x = Directions.Up;
  • 编译为javascript时
1
2
"use strict";
let x = 0 /* Directions.Up */;

6.8 联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。

1
2
3
let v1: number | string;
v1 = 5211314;
v1 = 'iloveyou'

6.9 断言

类型断言(Type Assertion)可以告诉编译器,“相信我,我知道自己在干什么,别报错,出了事我负责”。联合类型要有

1
2
3
4
let PI:number | string = 3.141592653
//断言PI是一个数,并且取小数后两位
console.log((PI as number).toFixed(2)) //3.14
console.log((<number>PI).toFixed(2)) //3.14

6.10 函数

TypeScript 为 JavaScript 函数添加了额外的功能

6.10.1 参数与返回值类型

1
2
3
4
5
6
7
8
9
10
11
function add1(x: number, y: number): number {
return x + y
}

let add2 = function(x: number, y: number): number {
return x + y
}

let add3 = (a: number, b: number): number => {
return a * b;
}

TypeScript 能够根据返回语句自动推断出返回值类型,因此我们通常省略返回值的类型。

6.10.2 可选参数

TypeScript 默认要求函数实参数量要与形参的数量保持一致,不过可以使用『 ?: 』设置参数为可选参数

1
2
3
4
//截取字符串
function slice(str: string, start: number, end ?: number): string{
return 'iloveyou';
}

6.10.3 参数默认值

TypeScript 与 JavaScript 一样,允许为参数设置默认值

案例: 为所有的手机添加默认值 ‘86’

1
2
3
4
//构建手机号
function buildPhone(code:string, area : string = '+86'){
return area + code;
}

7 class类型介绍

7.1 Type类型

type可以为任意类型创建别名,让代码更简洁、可读性更强,同时能更方便地进行类型复用和扩展。

1
2
3
4
5
6
7
type user = {
id: number;
name: string;
age: number;
}

export let myUser: user = {id:100,name:"张三",age: 300}

7.2 Class类型

  • 如果需要定义一个类型 方便以后统一使用 建议使用class类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export class User {
id:number;
name: string;
age: number;

constructor(id:number,name:string,age:number){
this.id = id
this.name = name
this.age = age
}

hello() {
console.log("你好:"+this.id + ":" + this.name + ":" + this.age)
}
}
  • vue中引入js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup lang="ts">

import { User } from './ts';

let user1 = new User(100,"张三",300)

console.log(user1)

user1.hello()

</script>

<template>
<div>
<h1>TS案例测试</h1>
</div>

</template>

<style scoped>

</style>

7.3 extends 关键字

  • 在JS中可以使用 extends 实现类的继承
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
export class User {
id:number;
name: string;
age: number;

constructor(id:number,name:string,age:number){
this.id = id
this.name = name
this.age = age
}

hello() {
console.log("你好:"+this.id + ":" + this.name + ":" + this.age)
}
}


export class SystemUser extends User {
username: string;
password: string;

constructor(id:number,name:string,age:number,username: string, password:string){
//使用super调用父类的方法
super(id,name,age)
this.username = username,
this.password = password
}

hello(){
console.log("你好:"+this.username + ":" + this.password)
}
}

7.4 属性修饰符

  • 在JS中也会有访问修饰符, 其中的效果和java中的一致
  • 默认类型: public
修饰符 含义 具体规则
public 公开的 可以被:类内部、**子类类外部**访问 。
protected 受保护的 可以被:类内部、**子类**访问。
private 私有的 可以被:**类内部**访问。
readonly 只读属性 属性无法修改。
  • 案例演示
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
export class Person {

public id:number;
protected name: string;
private readonly age: number; //其中readonly表示只读 不能被修改

constructor(id:number,name:string,age:number){
this.id = id
this.name = name
this.age = age
}
}

export class student extends Person {

//其中发现age 在类的外部不能使用
hello(){
console.log("你好"+this.id +":" +this.name + ":" +this.age)
}
}


let p1 = new Person(100,"李四",200)
//发现name在类的外部不能使用
console.log(p1.name)

7.5 class的简化写法

  • 可以使用构造方法简化class的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export class User {
id:number;
name: string;
age: number;

constructor(id:number,name:string,age:number){
this.id = id
this.name = name
this.age = age
}
}


export class User2 {
constructor(public id:number,public name:string,public age:number){}
}

7.6 抽象类

  • 概述:抽象类是一种**无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体实现。抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现**其中的抽象方法。
  • 简记:抽象类**不能实例化,其意义是可以被继承,抽象类里可以有普通方法、也可以有抽象方法**。
1
2
3
4
5
6
7
8
9
10
11
12
//定义抽象类
abstract class User {
//定义一个抽象方法 eat 并且指定返回值
abstract eat(foodName:string):string
}


export class myUser extends User {
eat(foodName: string): string {
return "炒着吃:"+foodName
}
}

7.7 Interface 接口

interface是一种**定义结构的方式,主要作用是为:类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,但要注意interface只能定义格式不能包含任何实现** !

  • 定义类结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1.定义一个接口
interface User {
//定义属性
id: number,
name:string,
age: number,
gender? :string //可选属性 可有可无

//定义函数
hello():void
}

//2.定义一个实现类 必须有对应的属性
class MyUser implements User {

constructor(public id:number,public name:string,public age:number,public gender:string){}

hello(): void {
console.log("你好世界")
}
}

  • 接口继承

接口可以被继承,如果继承接口 则需要实现所有的属性

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
//1.定义一个接口
interface User {
id: number,
name:string,
age: number,
gender? :string //可选属性 可有可无

//定义函数
hello():void
}

interface MyUser extends User {

username:string
password:string

}


class Person implements MyUser {
hello(): void {
throw new Error("Method not implemented.")
}

constructor(public username:string,public password:string,public id:number,public name:string,public age:number){

}

}

7.8 泛型

泛型允许我们在定义函数、类或接口时,使用类型参数来表示**未指定的类型,这些参数在具体使用时,才被指定具体的类型**,泛型能让同一段代码适用于多种类型,同时仍然保持类型的安全性。

  • 定义泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.定义常规函数
function logData(name:string):void{

console.log("页面代码:"+name)
}

logData("你好世界")
//发现参数必须是number类型
//logData(200)

//2.定义泛型 指定参数类型
function logData2<T>(name:T):void{

console.log("页面代码:"+name)
}

logData2(100)
logData2("这是泛型参数")
  • 定义泛型类型返回值
1
2
3
4
5
6
7
8
//3.指定泛型返回值
function logData3<T>(name:T):T{

console.log("这个函数必须要求返回类型返回")
return name
}

console.log(logData3("张三"))
  • 定义多个泛型
1
2
3
4
5
6
function logData3<T,R>(data1:T,data2:R): T|R {

return Date.now() % 2 ? data1 : data2
}

logData3(300,400)
  • 定义泛型接口
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
interface ServiceInterface<T> {

id: number,
name: string
data: T

}

interface Book {
bookName: string
}

interface User {
username: string,
password: string
}

let myService:ServiceInterface<Book> = {
id: 100,
name: "张三",
data: {
bookName: "java从入门到放弃"
}
}


let myService2:ServiceInterface<User> = {
id: 200,
name: "李四",
data: {
username: 'admin',
password: 'admin123'
}
}