当前位置: 首页 > news >正文

Typescript+React入门

初识Typescript

出现背景

Typescript(以下简称TS)实际上就是JavaScript+Type,用数据类型的方式来约束了JS的变量定义
在JS的基础上增加了类型支持
在这里插入图片描述
在JS中大多数错误都是因为数据类型造成的,所以TS为了规避这个问题加入了类型限制+编译检查,将问题在代码编译的时候(代码执行前)就可以发现错误

PS:TS是微软开发的,所以作为亲儿子,配合Vscode,TS 可以提前到在编写代码的同时就发现代码中的错误,减少找 Bug、改 Bug 时间

几个中大型框架对TS的支持:

  • Vue 3 源码使用 TS 重写
  • Angular 默认支持 TS
  • React 与 TS 完美配合

TypeScript 已成为大中型前端项目的首先编程语言

快速起步

TS默认是浏览器无法认识的,所以要通过编译器翻译成JS
在这里插入图片描述
新建一个文件夹在Vscode,初始化环境

// 安装ts环境
npm i -g typescript
// 查看ts版本(验证是否安装成功)
tsc -v

在这里插入图片描述

感受TS编译

新建一个hello.ts 的ts文件
在这里插入图片描述

通过命令编译ts文件,生成一个js文件

tsc hello.ts(此时,在同级目录中会出现一个同名的 JS 文件)

在这里插入图片描述

执行js代码node .\hello.js
在这里插入图片描述

简化编译步骤

每次都这么手动编译太累了,采用自动编译
简化方式:使用 ts-node 包,直接在 Node.js 中执行 TS 代码。
安装命令:npm i -g ts-node(ts-node 包提供了 ts-node 命令)
使用方式:ts-node hello.ts
解释:ts-node 命令在内部偷偷的将 TS -> JS,然后,再运行 JS 代码
报错解决方案

当然,真正工程里初始化是完美自动化的,具体实现可以自行百度

TS常用类型

前面我们知道了:
TypeScript 是 JS 的超集,TS 提供了 JS 的所有功能,并且额外的增加了类型系统(JS中,如果number在运行过程中突然变成了boolean是很常见的,所以也非常容易出问题)

类型注解

在哪用类型注解

示例:
在这里插入图片描述
如图所示:代码中的 : number 就是类型注解。

目的就是为了给age加上类型的约束,一旦变为其他的类型则报错
就会变成这样,so,约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错。
在这里插入图片描述

常用类型注解有哪些

可以将 TS 中的常用基础类型细分为两类:1 JS 已有类型 2 TS 新增类型

  1. JS 已有类型
    原始类型:number/string/boolean/null/undefined/symbol。
    对象类型:object(包括,数组、对象、函数等对象)。
  2. TS 新增类型
    联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等

原始类型

原始类型:number/string/boolean/null/undefined/symbol。
这些按照上面的写法来做即可
在这里插入图片描述

数组类型

对象类型:object(包括,数组、对象、函数等对象)

数组的两种写法:

//数组的定义方式这两种均可,更推荐第一个
let numbers0: number[] = [1, 2, 3];
let numbers1: Array<number> = [1, 2, 3];
//换了个数据类型
let numbers2: string[] = ["1", "2", "3"];
let numbers3: Array<string> = ["1", "2", "3"];

需求:数组里面我既要存number,又要存string,怎么办
| 来隔开数据类型

let numbers4: (number | string)[] = [1, "2", 3];
let numbers5: Array<number | string> = [1, "2", 3];

解释:| (竖线)在 TS 中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。
注意:这是 TS 中联合类型的语法,只有一根竖线,不要与 JS 中的或(||)混淆了

类型别名

类型别名(自定义类型):为任意类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用(相当于抽一个共通的类型出来)
语法:type 自定义类型名=(类型1|类型2|.....)[]注意最后有个数组符号
例子:

type userArray = (number | string)[];
let numbers6: userArray = [1, "2", 3];

解释:

  1. 使用 type 关键字来创建类型别名。
  2. 类型别名(比如,此处的 CustomArray),可以是任意合法的变量名称。
  3. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。

这里补一个知识,用type类型是可以定义方法头部的(类似接口)
type 函数名 = (param1: number, param2: string) => ReturnType;

//先用type来定义方法的参数列表,箭头函数后面是方法的返回值类型
//type 函数名 = (param1: number, param2: string) => ReturnType;
type F1 = (a: number) => number
// 定义f1,类型用F1约束好,利用箭头函数实现方法体
// 注意,这里只是按照type的约束把方法定义出来,调用的时候需要实例化或者去单独调用
let f1: F1 = (a: number):number => {// your code ...return a + 1;
}
//方法调用
let result = f1(111)
console.log(result)

函数类型(类似方法定义)

函数的类型实际上指的是:函数参数和返回值的类型。
为函数指定类型的两种方式:1 单独指定参数、返回值的类型 2 同时指定参数、返回值的类型。

单独指定参数、返回值的类型:

语法:

function 函数名(参数1: 类型, 参数2: 类型): 返回值类型{//方法体return 返回值(如果注明了返回值类型的话就代表有返回值);
}
//这两种定义方法的方法结果是一样的,区别在于定义的方式不同
//function是原始的js玩法,const是用的箭头函数,调用的时候也完全一样
function add(param1: number, param2: number): number {return param1 + param2;
}const add1 = (param1: number, param2: number): number => {return param1 + param2;
}add(1, 2)
add1(1, 2)

同时指定参数、返回值的类型:

同时指定参数、返回值的类型

const add2: (param1: number, param2: number) => number = (param1, param2) => {return param1 + param2;
}

解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。
注意:这种形式只适用于函数表达式

无返回值类型

如果函数没有返回值,那么,函数返回值类型为:void

function f1(name: string): void {console.log("userName:", name)
}

可选参数

使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。
比如,数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)
自定义一个测试类

function f2(name1?: string, name2?: string): void {console.log("name1:", name1, "name2:", name2);
}
//调用时传不传参数都可以
f2("aa", "bb");
f2("aa");

可选参数:在可传可不传的参数名称后面添加 ?(问号)
注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。也就是function f2(name1: string, name2?: string):void{ ... }

对象类型

JS 中的对象是由属性和方法构成的,而 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
对象类型的写法:

let obj1: {name: string,sayHi(): void} = {name: "张三",sayHi(): void { console.log("Hi~") },
}

解释:

  1. 直接使用 {} 来描述对象结构。属性采用属性名: 类型的形式;方法采用方法名(): 返回值类型的形式。
  2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如:greet(name: string): void)。
  3. 在一行代码中指定对象的多个属性类型时,使用 ;(分号)来分隔。
  • 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ;(分号)。

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

再扩展几个方法

let obj1: {name: string,age: number,sayHi(): void,sayName(name: string): void
} = {name: "张三",age: 18,sayHi(): void { console.log("Hi~") },sayName(name: string): void { console.log("name:", name) }
}

对象类型(参数可选情况)

对象的属性或方法,也可以是可选的,此时就用到可选属性了。
比如,我们在使用 axios({ … }) 时,如果发送 GET 请求,method 属性就可以省略。

function myAxios(config: { url: string, method?: string }) {//灵活应用,如果发送 GET 请求,method 属性就可以省略//打印的时候如果没传对应的参数就不打印了console.log(config)
}

可选属性的语法与函数可选参数的语法一致,都使用 ?(问号)来表示

接口

当对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,来达到复用的目的。
类似继承,方法不实现是不可以的,属性,方法都要具体定义。
在这里插入图片描述
修改后,对变量,方法做具体实现

// 定义接口,定义好属性
interface IPerson {name: string;age: number;sayHi(): void;
}
//使用接口类型约束后,需要具体实现
const obj: IPerson = {name: "张三",age: 18,sayHi(){console.log("Hi~")}
}

解释:

  1. 使用 interface 关键字来声明接口。
  2. 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称。
  3. 声明接口后,直接使用接口名称作为变量的类型。
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)。

interface和type的区别

接口更灵活
相同点:都可以给对象指定类型。

  • 不同点:接口,只能为对象指定类型。
  • 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名。
    在这里插入图片描述

接口复用

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
原有状态:

interface Point2D {x: number;y: number;
}interface Point3D {x: number;y: number;z: number;
}

比如,这两个接口都有 x、y 两个属性,重复写两次,可以,但很繁琐。
所以就引出了接口复用。
直接用extends来复用2D里面的内容

interface Point3D extends Point2D {z: number;
}
//集成后对值进行实现
const a: Point3D = {x: 1,y: 2,z: 3,
}

解释:

  1. 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D。
  2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)。

元组

场景:在地图中,使用经纬度坐标来标记位置信息。
可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。

//这样标记不严谨,没有具体约束有多少个元素
let position: number[] = [1, 2]
//使用元组进行约束,固定两个number元素的数组(当然,其他元素也完全可以,随便搞)
let position1: [number, number] = [1, 2]

元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型

解释:

  1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
  2. 该示例中,元素有两个元素,每个元素的类型都是 number。

类型推断

TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型。
换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写! 发生类型推论的 2 种常见场景:1 声明变量并初始化时 2 决定函数返回值时。
鼠标放在上面自动推断
在这里插入图片描述
在这里插入图片描述

类型断言

有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。(我断言这是一个xxx的类型)
用途一般都是获取某某标签,然后通过断言来获取标签属性
在这里插入图片描述
在这里插入图片描述
注意:getElementById 方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a
标签特有的 href 等属性。
因此,这个类型太宽泛(不具体),无法操作 href 等 a 标签特有的属性或方法。
解决方式:这种情况下就需要使用类型断言指定更加具体的类型。

使用类型断言:
明确的指明元素的类型

在这里插入图片描述
解释:

  1. 使用 as 关键字实现类型断言。
  2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)。
  3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了。

在这里插入图片描述

通过控制台打印

在这里插入图片描述

字面量类型

首先看下面两个变量类型
并不是都是string类型的

let str1='Hello TS'
const str2='Hello TS!'

通过 TS 类型推论机制,可以得到答案:

  1. 变量 str1 的类型为:string。
  2. 变量 str2 的类型为:‘Hello TS’。

解释:
3. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string。
4. str2 是一个常量(const),它的值不能变化,只能是 ‘Hello TS’,所以,它的类型为:‘Hello TS’。这个变量锁死了就只能是**‘Hello TS’**,const代表常量,不可变更

注意:此处的 ‘Hello TS’,就是一个字面量类型。也就是说某个特定的字符串也可以作为 TS 中的类型。 除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用。

使用模式:字面量类型配合联合类型一起使用。
使用场景:用来表示一组明确的可选值列表。
比如,在贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个(这里用到了枚举)。

//这里用了枚举,所以changeDirection的参数只能传'up' | 'down' | 'left' | 'right'中的一个
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {console.log(direction)
}
//只能传'up' | 'down' | 'left' | 'right'中的一个
changeDirection('up');

严格的类型约束
在这里插入图片描述

枚举

基础使用

上面字面量的场景,当字面量很多的情况下,就会显得很冗余
这里就换成枚举

枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。

在这里插入图片描述

  1. 使用 enum 关键字定义枚举。
  2. 约定枚举名称、枚举中的值以大写字母开头。
  3. 枚举中的多个值之间通过 ,(逗号)分隔。
  4. 定义好枚举后,直接使用枚举名称作为类型注解。

调用过程中,因为标记了只能传入枚举类型,so,传入的时候只能枚举.值
在这里插入图片描述

数字枚举

为什么叫数字枚举?
不难发现,枚举本身是没有对值进行定义的
而枚举的默认类型是number,并且第一个元素默认的值是0
第n个元素的默认值是n-1
在这里插入图片描述
在这里插入图片描述
这是数字枚举的默认情况,当然我们可以像Java里的枚举一样,进行默认值赋值处理。
如果有的枚举没有赋值,那么他就会默认继续自增下去
在这里插入图片描述

字符串枚举

看字面量就可以知道,字符串枚举内部的类型都是字符串(需要默认就给赋值,不赋值就会被默认为数字枚举)

在这里插入图片描述
如果有其中一个没有赋值,就会报错,因为:
字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。
在这里插入图片描述

枚举编译后的样子

在这里插入图片描述

any类型

首先说明:any类型不推荐在TS使用,因为any会把TS变成anyScript,
当一个变量被标记为any的时候,那么将不再会对其有任何提示

在这里插入图片描述
当用any标记之后,退化成JS了属于是😂
在这里插入图片描述
编译才会发现错误,违背了TS的早发现初衷
在这里插入图片描述
尽可能的避免使用 any 类型,除非临时使用 any 来“避免”书写很长、很复杂的类型!
其他隐式具有 any 类型的情况:

1 声明变量不提供类型也不提供默认值
2 函数参数不加类型。

注意:因为不推荐使用 any,所以,这两种情况下都应该提供类型!

typeof

在这里插入图片描述
在这里插入图片描述

实际上,TS 也提供了 typeof 操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。
使用场景:根据已有变量的值,获取该值的类型,来简化类型书写

未简化前状态
在这里插入图片描述
既然上面的let p和方法里面的point对象是一个类型的,那么就可以用typeof简化
这里需要指明关键字,直接标记是不可以的
在这里插入图片描述
优化后,使用typeof关键字
在这里插入图片描述
解释:

  1. 使用 typeof 操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同。
  2. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码)。
  3. 注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)

TS高级类型

class类

这个和Java类差不多

TypeScript 全面支持 ES2015 中引入的 class 关键字,并为其添加了类型注解和其他语法(比如,可见性修饰符等)

在这里插入图片描述

  1. 根据 TS 中的类型推论,可以知道 Person 类的实例对象 p 的类型是 Person。
  2. TS 中的 class,不仅提供了 class 的语法功能,也作为一种类型存在。

class类初始化

在这里插入图片描述
在这里插入图片描述

class构造函数

在这里插入图片描述
解释:

  1. 成员初始化(比如,age: number)后,才可以通过 this.age 来访问实例成员。
  2. 需要为构造函数指定类型注解,否则会被隐式推断为 any;构造函数不需要返回值类型。

此时再新建对象就可以直接指定值
在这里插入图片描述

class类的方法

在class类中提供一些方法,创建完成之后通过创建对象调用
在这里插入图片描述
解释:方法的类型注解(参数和返回值)与函数用法相同,正常传参调用即可。(当然,那个void可以不写,直接靠return自动类型推断也完全OK)

class类继承(extends)

这个是js就自带的,通过继承类,就可以调用父类的方法,通过继承获取父类的所有属性以及方法
在这里插入图片描述
解释:

  1. 通过 extends 关键字实现继承。
  2. 子类 Dog 继承父类 Animal,则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。
  3. Dog类新建对象调用方法

class类实现(implements)

这个是ts提供的,通过interface定义类,用implements来实现,在子类中对方法进行具体实现

在这里插入图片描述
解释:

  1. 通过 implements 关键字让 class 实现接口。
  2. sing类实现接口 Singable 意味着,sing 类中必须提供 Singable 接口中指定的所有方法和属性

class类访问权限控制

相比于Java的四种控制权限,TS只有3种,少了Java的default
类成员可见性:可以使用 TS 来控制 class 的方法或属性对于 class 外的代码是否可见。 可见性修饰符包括:1 public(公有的) 2 protected(受保护的) 3 private(私有的)。

public(公有的)

public:表示公有的、公开的,公有成员可以被任何地方访问,默认可见性
在哪都能访问到就不具体演示了
在这里插入图片描述
解释:

  1. 在类属性或方法前面添加 public 关键字,来修饰该属性或方法是共有的。
  2. 因为 public 是默认可见性,所以,可以直接省略。

protected(受保护的)

protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
在这里插入图片描述
解释:

  1. 在类属性或方法前面添加 protected 关键字,来修饰该属性或方法是受保护的。
  2. 子类的方法内部可以通过 this 来访问父类中受保护的成员,但是,对实例不可见!

private(私有的)

private:表示私有的,只在当前类中(方法内)可见,对实例对象以及子类也是不可见的。

在这里插入图片描述
解释:

  1. 在类属性或方法前面添加 private 关键字,来修饰该属性或方法是私有的。
  2. 私有的属性或方法只在当前类中的方法可见,对子类和实例对象也都是不可见的!

readonly(只读修饰符)

除了可见性修饰符之外,还有一个常见修饰符就是:readonly(只读修饰符)。
readonly:表示只读,用来防止在构造函数之外对属性进行赋值。
在这里插入图片描述
解释:

  1. 使用 readonly 关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法
  2. 注意:属性 age 后面的类型注解(比如,此处的 number)如果不加,则 age 的类型为 18 (字面量类型)
  3. 接口或者 {} 表示的对象类型,也可以使用 readonly来修饰变量

类型兼容性

先说个梗,如果一个东西,走路像鸭子,长得像鸭子,吃饭像鸭子,那他就是个鸭子
两种类型系统:1 Structural Type System(结构化类型系统) 2 Nominal Type System(标明类型系统)。

为了帮助理解,这种其实是类似于多态,很大程度上利用了class、方法的兼容

Structural Type System(结构化类型系统)

TS 采用的是结构化类型系统,也叫做 duck typing(鸭子类型),类型检查关注的是值所具有的形状。 也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型(它看起来就是鸭子)。

这两个可以被认为是同一类型
在这里插入图片描述

那么point和point2的两个对象的结构是相同的,因此认为是“同一类型”,在new对象的时候就可以这样

//这里之所以可以这么创建,是因为point和point2的两个对象被认为是同一类型
//这里point代表p的类型,point2()代表创建point2类型的实例
const p: point = new point2()

解释:

  1. Point 和 Point2D 是两个名称不同的类。
  2. 变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。
  3. 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同,结构相同就认为是同一类型(相同,都具有 x 和 y 两个属性,属性类型也相同)。
  4. 但是,如果在 Nominal Type System 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。

结构化类型系统向下兼容

注意:在结构化类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型,这种说法并不准确。 更准确的说法:对于对象类型来说,y 的成员至少与 x 相同,则 x 兼容 y(成员多的可以赋值给少的)。

在这里插入图片描述
反过来的话肯定不兼容
在这里插入图片描述
解释:

  1. Point3D 的成员至少与 Point 相同,则 Point 兼容 Point3D。
  2. 所以,成员多的 Point3D 可以赋值给成员少的 Point(只要能全部满足少的一方,多加多少都可以)。

接口兼容性

除了 class 之外,TS 中的其他类型也存在相互兼容的情况,包括:1 接口兼容性 2 函数兼容性 等。
接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。

interface之间可以直接使用类型兼容,写法和class一样
在这里插入图片描述
interface与class之间也可以兼容
在这里插入图片描述

函数兼容性

函数之间兼容性比较复杂,需要考虑:1 参数个数 2 参数类型 3 返回值类型


  • 参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的,和对象刚好相反)。
//用type来定义函数
type 函数名 = (param1: number, param2: string) => ReturnType;
//先用type来定义方法的参数列表,箭头函数后面是方法的返回值类型
//type 函数名 = (param1: number, param2: string) => ReturnType;
type F1 = (a: number) => void
// 定义f1,类型用F1约束好,利用箭头函数实现方法体
// 注意,这里只是定义,调用需要实例化或者单独调用方法
let f1: F1 = (a: number): void => {// your code ...
}
// 定义F2的方法体,比F1的要多一个参数
type F2 = (a: number, b: number) => void
//将F1的方法比F2的方法要少一个参数,所以可以兼容
let f2: F2 = f1
//方法调用
let result = f2(111)
console.log(result)

再比如说数组的forEach方法,可以选择传一个参数,也可以一个不传
在这里插入图片描述
解释:

  • 参数少的可以赋值给参数多的,所以,f1 可以赋值给 f2。
  • 数组 forEach 方法的第一个参数是回调函数,该示例中类型为:(value: string, index: number, array: string[]) => void。
  • 在 JS 中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了 TS 中函数类型之间的兼容性。
  • 并且因为回调函数是有类型的,所以,TS 会自动推导出参数 item、index、array 的类型。

  • 参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)
    在这里插入图片描述
    解释:函数类型 F2 兼容函数类型 F1,因为 F1 和 F2 的第一个参数类型相同。

来个复杂点的:
先用interface定义参数类型
在type定义参数列表的时候用上interface做定义
先定义了f2,再去赋给f3,但是f3反过来赋给f2不可以
因为参数列表无法小范围 的去兼容 大范围
在这里插入图片描述
解释:

  1. 注意,此处与前面讲到的接口兼容性冲突。
  2. 技巧:将对象拆开,把每个属性看做一个个参数,则,参数少的(f2)可以赋值给参数多的(f3)。

  • 返回值类型,只关注返回值类型本身即可:
    在这里插入图片描述
    解释:
  1. 如果返回值类型是原始类型,此时两个类型要相同,比如,左侧类型 F5 和 F6。
  2. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,右侧类型 F7 和 F8。

交叉类型

交叉类型(&),有点类似于接口继承(extends),用于多个类型组合为一个类型

//定义两个接口类型
interface Person {name: string;
}
interface Man {age: string;
}
//用type来交叉两个类型
type PersonAndMan = Person & Man;
const p: PersonAndMan = {//获取到Person的name属性name: "zhangsan",//获取到Man的age属性age: "lisi",
};

解释:当使用了交叉类型之后,type PersonAndMan就拥有了PersonMan的两个类型


交叉类型(&)与继承(extends)的对比

  • 相同点:都可以实现对对象类型的组合
  • 不同点:两种方式实现类型组合时,对于同名属性之间出现冲突(如果同名同属性,则视为同一个,如果同名不同属性,则会冲突),处理类型冲突的方式不同

在这里插入图片描述


泛型

初识泛型

泛型时可以保证类型安全的前提下,让函数等与多种类型一起工作,从而实现灵活复用,常用于函数、接口、class中

需求:创建一个id函数,传入什么数据就返回数据本身(参数和返回值类型一样)
比如这个函数,只能传递number类型并且返回number类型,无法用于其他类型

function id(param: number): number{return param;
}

要是稍微改造一下,把类型换成any。这样确实可以接受其他类型,但代价是失去了TS的类型保护,不安全

function id(param: any): any {return param;
}

此时的解决方案就是泛型
泛型再保证类型安全(不丢失类型信息)的同时,可以让函数可以传入多种不同的类型,实现灵活复用。

创建一个泛型函数
在这里插入图片描述
实操一下

//将函数得参数类型定义为Type
function test<Type>(value: Type): Type {return value;
}
//同样的函数传入不同的类型,不会报错,完成同一个函数复用的目的
const m1: number = test(20);
const m2: string = test("aa");
const m3 = test(20);
const m4 = test("aa");

解释:
1.再调用泛型函数时,可以省略<类型>来简化泛型函数的调用
2.此时TS的内部会采用类型参数推断机制,来根据传入的实参自动推断出类型变量Type的类习惯
3.比如此时传入实参20,TS会自动推断出传入的参数时number,此时Type就会自动变成number类型
推荐:使用这种简化的方式调用泛型函数,使代码更短更易于阅读
说明:当编译器无法推断类型或者推断类型不够准确的时候,就需要显式的传入参数类型参数

泛型约束

问题描述
默认情况下,由于Type类型可以代表的类型太多,导致无法访问任何属性
在这里插入图片描述

泛型收缩的方式

添加泛型约束来收缩类型,主要有两种方式:1 指定更加具体的类型 2 添加约束

  • 1.指定更加具体的类型
function test2<Type>(value: Type[]): Type[] {console.log(value.length);return value;
}

比如,将类型修改为Type[](Type类型的数组),因为只要是数组就一定存在length属性,因此就可以访问了。

  • 2.添加约束
    创建 描述约束的接口ILength,该接口要求提供length属性
    通过继承extends关键字来使用该接口,为泛型(类型变量)添加约束
    该约束表示,传入的类型中必须有length属性
    注意,这里传入的实参(比如数组,对象),只要有length属性即可,并且也符合前面讲到的接口类型兼容性
//指定一个number类型的Type
interface ILength {length: number;
}
//Type利用extends来约束类型
function test3<Type extends ILength>(param: Type): Type {console.log(param.length);return param;
}

多个类型变量:

类型变量间约束

在这里插入图片描述

泛型接口

//定义接口类型为Type,内部的类型都可以根据Type来变化
interface IdFunc<Type> {id: (value: Type) => Type;ids: () => Type[];
}
//新建变量实现接口,接口传入number
let obj: IdFunc<number> = {id(value) {return value;},ids() {return [1, 3, 5];},
};
//调用方法...
obj.id(1);

解释:
1.在interface后面添加<类型变量>,那么该接口就变成了泛型接口
2.interface的类型变量,对接口中所有其他成员都可见,也就是接口中所有成员都可以用类型变量
3.使用泛型接口的时候,需要显式的标注具体类型,比如:interface IdFunc<Type> {...}
4.通过变量实现接口后let obj: IdFunc<number> = {},obj的变量和方法都会受到传入number变量的影响。
比如 id: (value: Type) => Type;在obj的实现中就会变成 id: (value: number) => number;
比如 ids: () => number[];在obj的实现中就会变成 ids: () => number[];

实际上,JS中的数组在TS中,就是一个泛型接口,根据传入的不同类型变化为不同的数组类型
每次foreach的方法参数类型也会不一样
在这里插入图片描述

泛型类

class也可以配合泛型来使用

比如React中的class组件的基类Component就是泛型类,不同的组件有不同的props和state

在这里插入图片描述

创建一个泛型类
在创建类型的时候明确指定创建对象类型
在这里插入图片描述
解释:
1.类似于泛型接口,在class名称后面添加 <类型变量>,这个类就变成了泛型类
2.此处的add方法,采用的是箭头函数形式的类型书写方法

泛型工具类

泛型工具类型:TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作。 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。
这些工具类型有很多,主要学习以下几个:

Partial<Type>
Readonly<Type>
Pick<Type, Keys>
Record<Keys, Type>

Partial< Type>可选

泛型工具类型Partial用来构造(创建)一个类型,将 Type 的所有属性设置为可选
构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的

interface A {id: stringchildren: number[]
}
//定义Props的Type,结构与interface A一致
//但是所有属性都变为可选的
type Props = Partial<A>
//定义obj对象,结构可以与A一样
let obj: Props = {id: "123",children: [1, 2, 3]
}

ReadOnly< Type>只读

泛型工具类型 - Readonly 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
构造出来的新类型 ReadonlyProps 结构和 A 相同
在这里插入图片描述

Pick<Type, Keys>可选

泛型工具类型 Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型

interface A {id: stringage: stringchildren: number[]
}
//定义一个Pick的类,选择A接口中的两个属性
type ReadonlyType = Pick<A, 'id' | 'age'>
//到这里为止,实际上只能使用A接口的id与age属性
let obj: ReadonlyType = {id: "111",age: "18"
} 

如果强行加入只会报错
在这里插入图片描述

  1. Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。
  2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
  3. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
  4. 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。

Record<Keys,Type>构造

泛型工具类型Record<Keys,Type> 构造一个对象类型,属性键为 Keys,属性类型为 Type。

Record 工具类型有两个类型变量:1 表示对象有哪些属性 2 表示对象属性的类型。

//用Record来记录类型
//规定RecordObj有'a' | 'b' | 'c'三个属性,均为string数组
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj: RecordObj = {a: ['1'],b: ['2'],c: ['3']
}
//规定RecordObj2有'a' | 'b' | 'c'三个属性,均为string类型
type RecordObj2 = Record<'a' | 'b' | 'c', string>
let obj2: RecordObj2 = {a: '1',b: '2',c: '3'
}

索引签名类型

绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。
使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了。
在这里插入图片描述
解释:

  1. 使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称,都可以出现在对象中。number代表属性只能赋number类型
  2. 这样,对象 obj 中就可以出现任意多个属性(比如,a、b 等)。
  3. key 只是一个占位符,可以换成任意合法的变量名称。
  4. 隐藏的前置知识:JS 中对象({})的键是 string 类型的。

数组索引签名

在 JS 中数组是一类特殊的对象,特殊在数组的键(索引)是数值类型。
并且,数组也可以出现任意多个元素。所以,在数组对应的泛型接口中,也用到了索引签名类型。
在这里插入图片描述
解释:

  1. MyArray 接口模拟原生的数组接口,并使用 [n: number] 来作为索引签名类型。
  2. 该索引签名类型表示:只要是 number 类型的键(索引)都可以出现在数组中,或者说数组中可以有任意多个元素。
  3. 同时也符合数组索引是 number 类型这一前提。

映射类型

映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。(简单说就是把属性名都给copy过来,具体类型重新定义)

在这里插入图片描述
解释:

  1. 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了 []。
  2. Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin(let k in obj)。
  3. 使用映射类型创建的新对象类型 Type2 和类型 Type1 结构完全相同。
  4. 注意:映射类型只能在类型别名中使用,不能在接口中使用。

其他的一些情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

TypeScript 类型声明文件

今天几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。
这些第三方库不管是否是用 TS 编写的,最终都要编译成 JS 代码,才能发布给开发者使用。
我们知道是 TS 提供了类型,才有了代码提示和类型保护等机制。
但在项目开发中使用第三方库时,你会发现它们几乎都有相应的 TS 类型,这些类型是怎么来的呢?类型声明文件
类型声明文件:用来为已存在的 JS 库提供类型信息。 这样在 TS 项目中使用这些库时,就像用 TS 一样,都会有代码提示、类型保护等机制了。

TS中的两种文件类型

TS 中有两种文件类型:1 .ts 文件 2 .d.ts 文件
.ts 文件:

  1. 既包含类型信息又可执行代码。
  2. 可以被编译为 .js 文件,然后,执行代码。
  3. 用途:编写程序代码的地方。

.d.ts 文件:

  1. 只包含类型信息的类型声明文件。
  2. 不会生成 .js 文件,仅用于提供类型信息。
  3. 用途:为 JS 提供类型信息。

总结:.ts 是 implementation(代码实现文件);.d.ts 是 declaration(类型声明文件)。

如果要为 JS 库提供类型信息,要使用 .d.ts 文件

使用已有的类型声明文件

  • 1 内置类型声明文件
    在这里插入图片描述

  • 2 第三方库的类型声明文件。

库自带类型声明文件
在这里插入图片描述
由 DefinitelyTyped 提供
在这里插入图片描述

自己创建声明文件并使用

项目内共享类型

如果多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享。
在这里插入图片描述

操作步骤:

  1. 创建 utils.d.ts 类型声明文件。
  2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
  3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

为已有 JS 文件提供类型声明

在这里插入图片描述
相当于加载js文件时自动转换为.d.ts文件
在这里插入图片描述

React中使用TS

使用 CRA 创建支持 TS 的项目

看起来CRA是什么很屌的工具是吧,实际不是
说白了就是个一件创建React工程的
React 脚手架工具 create-react-app(简称:CRA)默认支持 TypeScript。
创建支持 TS 的项目命令:create-react-app my-react-app 项目名称my-react-app
当看到以下提示时,表示支持 TS 的项目创建成功:
在这里插入图片描述

相比于非TS的项目的区别

相对于非 TS 项目,目录结构主要由以下三个变化:

  1. 项目根目录中增加了 tsconfig.json 配置文件:指定 TS 的编译选项(比如,编译时是否移除注释)。
  2. React 组件的文件扩展名变为:*.tsx。
  3. src 目录中增加了 react-app-env.d.ts:React 项目默认的类型声明文件。
    在这里插入图片描述
    解读一下react-app-env.d.ts:React 项目默认的类型声明文件。
    三斜线指令:指定依赖的其他类型声明文件,types 表示依赖的类型声明文件包的名称
    在这里插入图片描述
    解释:告诉 TS 帮我加载 react-scripts 这个包提供的类型声明。
    react-scripts 的类型声明文件包含了两部分类型:
  4. react、react-dom、node 的类型
  5. 图片、样式等模块的类型,以允许在代码中导入图片、SVG 等文件。
    TS 会自动加载该 .d.ts 文件,以提供类型声明(通过修改 tsconfig.json 中的 include 配置来验证)

TS配置文件tsconfig.json

tsconfig.json 指定:项目文件和项目编译所需的配置项。 注意:TS 的配置项非常多(100+),以 CRA 项目中的配置为例来学习,其他的配置项用到时查文档即可。

  1. tsconfig.json 文件所在目录为项目根目录(与 package.json 同级)。
  2. tsconfig.json 可以自动生成,命令:tsc --init一般需要手动生成

可以看到很多的配置被注掉,选择需要的解开即可
在这里插入图片描述
除了在 tsconfig.json 文件中使用编译配置外,还可以通过命令行来使用。
使用演示:tsc hello.ts --target es6。
这个的意思是 编译hello.ts文件,以es6的版本编译
注意:

  1. tsc 后带有输入文件时(比如,tsc hello.ts),将忽略 tsconfig.json 文件。
  2. tsc 后不带输入文件时(比如,tsc),才会启用 tsconfig.json。
    推荐使用:tsconfig.json 配置文件。

React 中的常用类型

React 是组件化开发模式,React 开发主要任务就是写组件
两种组件:1 函数组件 2 class 组件。

函数组件,主要包括以下内容:

  • 组件的类型
  • 组件的属性(props)
  • 组件属性的默认值(defaultProps)
  • 事件绑定和事件对象

函数组件

函数组件创建

注意,react的组件,用的是.tsx文件,如果只用.ts文件,是用不了H5的组件的!

一定一定要看一下这个文章:React+TS工程初始化的问题

import React, { FC } from "react";
import ReactDOM from "react-dom";//定义一个Props的接口
interface Props {name: string;age: number;
}//Hello组件,允许传入Props类型参数
//其实不写React.FC也可以。React.FC表示:React.Function Component。
//React.FC 显式地定义了返回类型,作为一个组件返回,其他方式是隐式推导的。
const Hello: React.FC<Props> = ({ name, age }) => (<div>名字{name}年龄{age}</div>
)
//Test组件,没有参数
const Test: React.FC = () => (<div>这里是一个Test组件</div >
)//创建App作为整个页面的基础
const App = () => {return (<div>{/* 组件传值 */}<Hello name={'zs'} age={18} /><Test></Test></div>)}
//将<App />渲染到root上
ReactDOM.render(<App />, document.getElementById('root'))

关于那个传参的,还是可以再简化一下
在这里插入图片描述

函数组件属性的默认值(defaultProps)

第一种写法,有React.FC的显式标注,比较麻烦,换第二种

// 比如我想给组件的某个属性赋一个固定值
const Hello: React.FC<Props> = ({ name, age }) => (<div>名字{name}年龄{age}</div>
)
//赋默认值
Hello.defaultProps = {age: 18
}

第二种写法,直接在参数列表上赋值,完全按照函数在 TS 中的写法:

//直接在参数上赋值
const Hello = ({ name, age = 18 }: Props) => (<div>名字{name}年龄{age}</div>
)

事件绑定和事件对象

事件绑定和事件对象

//Test组件,没有参数
const Test: React.FC = () => (<div>{/* 绑定点击操作 */}<button onClick={onclick}>点击</button></div >
)
//绑定事件
const onclick = () => {console.log("点击操作")
}
//这里绑定的是鼠标事件操作
const onclick1 = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {console.log("点击操作")
}//创建App作为整个页面的基础
const App = () => {return (<div><Test></Test></div>)
}
//将<App />渲染到root上
ReactDOM.render(<App />, document.getElementById('root'))

绑定事件写法
再比如,文本框:

//Test组件,没有参数
const Test: React.FC = () => (<div>{/* 绑定修改输入框操作 */}<input onChange={onchange} /></div >
)
//这里绑定的是修改事件操作
const onchange = (e: React.ChangeEvent<HTMLInputElement>) => {console.log("输入框发生变化")
}

查看可以绑定的事件

在这里插入图片描述
实际操作:
在这里插入图片描述
确定好可以绑定的事件之后,就可以进行事件绑定

//这里绑定的是输入变化操作
const onchange = (e: React.ChangeEvent<HTMLInputElement>) => {console.log("输入框发生变化")
}

class组件

class 组件,主要包括以下内容:
组件的类型、属性、事件 (props)
组件状态(state)

class组件属性(props)

Props(属性):
props 是 React 组件的一种机制,用于向组件传递数据。它是从父组件传递给子组件的数据,而子组件不能直接修改 props,只能读取其中的数据。因此,props 是用于组件之间通信的一种方式。
在使用组件时,可以在组件标签上添加属性,这些属性将被封装成 props 对象传递给组件。在组件内部,通过解构或直接访问 props 对象,可以获取传递的数据,然后在组件中使用这些数据。

//定义一个Props的接口
type A = {name: string;age?: number;
}
// React.Component<Props,State>,这里只传入Props不传入State
class Test extends React.Component<A, {}> {//将A的age属性赋默认值static defaultProps: Partial<A> = {age: 18}render() {//通过this.props获取值,当然,也可以在这上面直接赋值const { name, age = 20 } = this.props;return <><div>名字{name},年龄{age}</div></>}
}//创建App作为整个页面的基础
const App = () => {return (<div><Test></Test></div>)}
//将<App />渲染到root上
ReactDOM.render(<App />, document.getElementById('root'))

class组件状态(state)

State(状态):
state 是 React 组件用于管理自己的内部状态的一种机制。通过使用 useState 或 useReducer 等 React 提供的钩子或类组件的 setState 方法,可以在组件内部创建和管理状态。
与 props 不同,state 是组件私有的,只能在组件内部访问和修改。当 state 发生改变时,React 将会自动更新组件,并重新渲染显示新的状态。

//定义State名字的type组件
type State = {count: number
}class Counter extends React.Component<{}, State>{//将State赋默认值static: State = {count: 20//number: 30 因为没有State没有number,所以无法赋值}//定义方法,state是组件内部使用的onIncrement = () => {this.setState({//改变count的值count: this.state.count + 1})}render() {//这里定义返回组件,调用组件定义的自增函数return <><div><button onClick={this.onIncrement}>+1</button></div></>}
}//创建App作为整个页面的基础
const App = () => {return (<div><Counter></Counter></div>)
}
//将<App />渲染到root上
ReactDOM.render(<App />, document.getElementById('root'))

总结React.Component 的 Props和State

props 是组件之间进行数据传递的一种机制,用于从父组件向子组件传递数据。
state 是组件内部维护的状态,用于管理组件的变化和更新。

props 是只读的,组件不能直接修改传递给它的 props。
state 是可变的,可以通过特定的方法修改组件的 state,触发组件的重新渲染。

=======================================================
对于React.Component来说,参数是可选的
定义两个参数type

type State = {count: number
}
type Props = {message: string
}

在这里插入图片描述

相关文章:

Typescript+React入门

初识Typescript 出现背景 Typescript&#xff08;以下简称TS&#xff09;实际上就是JavaScriptType&#xff0c;用数据类型的方式来约束了JS的变量定义 在JS的基础上增加了类型支持 在JS中大多数错误都是因为数据类型造成的&#xff0c;所以TS为了规避这个问题加入了类型限制…...

竞赛项目 酒店评价的情感倾向分析

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 酒店评价的情感倾向分析 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/post…...

加载并绘制时间域内的心电图信号,并实施Q因子为1的陷波滤波器以去除50 Hz频率研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

瑞数信息《2023 API安全趋势报告》重磅发布: API攻击持续走高,Bots武器更聪明

如今API作为连接服务和传输数据的重要通道&#xff0c;已成为数字时代的新型基础设施&#xff0c;但随之而来的安全问题也日益凸显。为了让各个行业更好地应对API安全威胁挑战&#xff0c;瑞数信息作为国内首批具备“云原生API安全能力”认证的专业厂商&#xff0c;近年来持续输…...

HCIA静态路由与动态路由

目录 一、静态路由 定义&#xff1a; 适用环境 二、动态路由 定义&#xff1a; 特点&#xff1a; 动态路由协议: 三、缺点&#xff1a; 1&#xff09;静态路由缺点: 2&#xff09;动态路由的缺点: 四、静态路由与动态路由的区别 静态路由: 动态路由: 一、静态路…...

【前端 | CSS】flex布局

基本概念 Flexible模型&#xff0c;通常被称为 flexbox&#xff0c;是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力 我们说 flexbox 是一种一维的布局&#xff0c;是因为一个 flexbox 一次只能处理一个维度上的元素布局&#xff0c;一行或者…...

YoloV8优化:感受野注意力卷积运算(RFAConv),效果秒杀CBAM和CA等 | 即插即用系列

💡💡💡本文改进:感受野注意力卷积运算(RFAConv),解决卷积块注意力模块(CBAM)和协调注意力模块(CA)只关注空间特征,不能完全解决卷积核参数共享的问题 RFAConv| 亲测在多个数据集能够实现大幅涨点,有的数据集达到3个点以上 💡💡💡Yolov8魔术师,独家首…...

面对AI冲击,技术人才该如何考核?

一天下午&#xff0c;在与知名企业的技术交流会议室里&#xff0c;一位兄弟企业的CTO 小力苦笑着&#xff0c;分享了一个技术招聘的故事&#xff1a; “我们有个高级工程师&#xff0c;为了搞定MySQL三个表Join的问题&#xff0c;搞了一整天都研究不出来。结果他尝试将表结构扔…...

放弃51单片机,直接学习STM32开发可能会面临的问题

学习51单片机并非仅仅是为了学习51本身&#xff0c;而是通过它学习一种方法&#xff0c;即如何仅仅依靠Datasheet和例程来学习一种新的芯片。51单片机相对较简单&#xff0c;是这个过程中最容易上手的选择&#xff0c;而AVR单片机则更为复杂。虽然您已经学习了大约十天的51单片…...

windows安装git并初始化

git官网下载地址&#xff1a; https://git-scm.com/downloads 安装步骤&#xff0c;一直点击下一步即可 git初始化 1、用户签名 git config --global user.email 2734542837qq.com#设置全局用户邮箱git config --global user.name "zoujiahao"# 设置全局用户使用人…...

SpringBoot集成websocket(3)|(websocket调用websocket采用回调方式实现数据互传)

SpringBoot集成websocket&#xff08;3&#xff09;|&#xff08;websocket调用websocket采用回调方式实现数据互传&#xff09; 文章目录 SpringBoot集成websocket&#xff08;3&#xff09;|&#xff08;websocket调用websocket采用回调方式实现数据互传&#xff09;[TOC] 前…...

基于Doris实时数据开发的一些注意事项

300万字&#xff01;全网最全大数据学习面试社区等你来&#xff01; 最近Doris的发展大家是有目共睹的。例如冷热分离等新特性的持续增加。使得Doris在易用和成本上都有大幅提升。 基于Doris的一些存储实时数仓在越来越多的场景中开始有一些实践。大家也看到了这种方案频繁出现…...

竞赛项目 深度学习疲劳驾驶检测 opencv python

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…...

20.4 HTML 表单

1. form表单 <form>标签: 用于创建一个表单, 通过表单, 用户可以向网站提交数据. 表单可以包含文本输入字段, 复选框, 单选按钮, 下拉列表, 提交按钮等等. 当用户提交表单时, 表单数据会发送到服务器进行处理.action属性: 应指向一个能够处理表单数据的服务器端脚本或UR…...

Linux——基础IO(1)

目录 0. 文件先前理解 1. C文件接口 1.1 写文件 1.2 读文件 1.3 输出信息到显示器 1.4 总结 and stdin & stdout & stderr 2. 系统调用文件I/O 2.1 系统接口使用示例 2.2 接口介绍 2.3 open函数返回值 3. 文件描述符fd及重定向 3.1 0 & 1 & 2 3.2…...

MFC第二十七天 通过动态链表实现游戏角色动态增加、WM_ERASEBKGND背景刷新的原理、RegisterClass注册窗口与框架程序开发

文章目录 通过动态链表实现游戏角色动态增加CMemoryDC.hCFlashDlg.hCFlashDlg.cpp WM_ERASEBKGND背景刷新的原理RegisterClass注册窗口与框架程序开发CFrameRegister 通过动态链表实现游戏角色动态增加 CMemoryDC.h #pragma once#include "resource.h"/*内存DC类简介…...

Debezium系列之:基于内容路由实现把数据库表中的数据按照数据类型分发到不同的topic

Debezium系列之:基于内容路由实现把数据库表中的数据按照数据类型分发到不同的topic 一、需求背景二、创建表三、插入、更新、删除数据四、核心参数和实现技术五、查看分发的Topic六、消费Topic数据七、总结和延展一、需求背景 一张表中存有各个超市门店的订单信息,例如超市门…...

苹果账号被禁用怎么办?

苹果账号被禁用怎么办&#xff1f; 转载&#xff1a;苹果账号被禁用怎么办&#xff1f; 当我们使用苹果手机登录App Store时&#xff0c;有时会遇到账号被禁用的提示。总结下来&#xff0c; 账号被禁用的原因可能有以下几种&#xff1a; 禁用的原因 1.在不同的设备上登录Ap…...

文章一:快速上手Git - 从零到一:Git版本控制入门指南

开始本篇文章之前先推荐一个好用的学习工具&#xff0c;AIRIght&#xff0c;借助于AI助手工具&#xff0c;学习事半功倍。欢迎访问&#xff1a;http://airight.fun/。 概述 在软件开发和团队协作中&#xff0c;版本控制是一项至关重要的技术。Git作为现代开发者最喜爱的版本控…...

【用unity实现100个游戏之6】制作一个战旗自走棋类游戏(附源码)

文章目录 前言导入素材开始1. 设置瓦片间隙2. 放置全图瓦片3. 美化瓦片地图4. 添加树木障碍物5. 设定不同的排序图层6. 瓦片交互6. 瓦片交互优化6. 瓦片是否允许角色7. 添加角色8. 新增游戏管理脚本9. 角色移动范围逻辑10. 角色移动范围可视化11. 角色移动12. 重置瓦片颜色12. …...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...