TypeScript学习笔记(全)

文章目录

  • TypeScript入门
    • 2.编译并运行TS代码
      • 2.1.简化运行ts步骤
    • 3.TS中的常用类型
    • 4.TS中的高级类型
      • 4.1.class类
      • 4.2.class的构造函数
      • 4.3.class类的实例方法
      • 4.4.class继承
        • extends
        • implements
      • 4.5.类的可见性修饰符
        • 公有的(public)
        • 受保护的(protected)
        • 私有的(private)
      • 4.6.readonly只读属性
      • 4.7.类型兼容性
      • 4.8.对象之间的类型兼容性
      • 4.9.接口之间的兼容性
      • 4.10.函数之间的兼容性
        • 参数个数
        • 函数参数
        • 返回值
          • 返回值是原始类型
          • 返回值是对象类型
      • 4.3.交叉类型
      • 4.4.交叉类型 和 接口继承 之间的比对
      • 4.5.*泛型
        • 创建泛型函数
        • 调用泛型函数
        • 简化函数的调用
        • 泛型约束
          • 指定更加具体的类型
          • **添加约束**
          • *多个泛型变量情况
        • 泛型接口
        • 数组是泛型接口
        • 泛型类
        • 泛型工具类
          • Patial
          • Readonly
          • Pick
          • Record
      • 4.6.索引签名类型
      • 4.7.映射类型
      • 4.8.映射类型(keyof)
      • 4.9.索引查询类型
        • 查询单个
        • 查询多个
      • 5.**类型声明文件
        • TS中的两种文件类型
        • 类型声明文件的使用说明
      • 5.1.创建自己的类型声明文件
          • 项目内共享类型
          • 为已有的JS文件提供类型声明
          • declare关键字

TypeScript入门

2.编译并运行TS代码

  1. 创建hello.ts文件

    1. const info : string = 'hello ts';console.log(info)
  2. 将TS文件编译为JS文件,在终端中输入命令 tsc .\hello.ts (此时该目录下会生成一个同名的JS文件)

    1. image-20240506074107694
  3. 执行JS代码,在终端中输入命令 node hello.js (即可执行刚刚的js文件)

    1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要注意的是,TS编译生成的JS文件中,代码中就没有类型的信息了。

image-20240506074614754

2.1.简化运行ts步骤

简化方式 通过ts-node包,可以直接在node中运行ts代码,不用每次再使用ts进行编译 在使用node进行运行

npm i -g ts-node# 使用 就可以实现编译运行两步操作
# 注意 ts-node 并没有生成js文件,他在内部偷偷帮你转换,并且运行
ts-node hello.ts 

image-20240506075012630

3.TS中的常用类型

在TypeScript(TS)中,类型系统是其核心特性之一,为JavaScript增添了静态类型检查的能力,从而提高代码的可维护性和安全性。

也可以将TS中测常用基础类型细分为两类:

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

以下是TypeScript中一些常用且重要的类型:

  1. 基本类型

    • string:用于文本字符串。
    • number:用于数值,包括整数和浮点数。
    • boolean:布尔值,只能是truefalse
    • nullundefined:表示空值或未定义的值,TypeScript 中它们是所有类型的子类型。
    • void:表示没有任何返回值的函数。
    • never:表示永远不会出现的值的类型,常用于抛出异常或无限循环的函数。
  2. 数组类型

    • 使用Array<元素类型>元素类型[]定义,例如number[]表示一个数字数组。
  3. 元组(Tuple)

    • 定义固定长度和类型的数组,例如[string, number]表示一个数组,其第一个元素为字符串,第二个元素为数字。
  4. 对象类型(Object)

    使用接口(interface)或类型别名(type)来描述对象结构,如:

    interface Person {name: string;age: number;
    }
    
  5. 枚举(Enum)

    • 用于定义一组命名的常量,如:

      1enum Color {Red, Green, Blue}
      
  6. 任意类型(any)

    • 表示可以是任何类型,使用时需谨慎,因为它绕过了类型检查。
  7. 联合类型(Union Types)

    • 使用管道符号|分隔,表示一个值可以是多种类型中的一种,如string | number
  8. 类型断言(Type Assertion)

    • 用来告诉编译器某个值的类型,形式为value as Type
  9. 字面量类型(Literal Types)

    • 直接使用具体的值作为类型,如const answer: 42 = 42;
  10. 索引签名(Index Signatures)

    • 用于定义对象中动态属性的类型,如{ [key: string]: any }
  11. 类(Class)和接口

    • 类用于创建对象的蓝图,接口用于定义类或对象的形状。
  12. 泛型(Generics)

    • 提供了编写可重用组件的方式,这些组件可在多种数据类型上工作,如Array<T>

3.1.TS中的类型注解

示例代码:

// 只需要在 变量后面 : 具体的类型 即可
let age : number = 19

代码中的 number 类型就是类型的注解,作用就是为变量添加类型约束,比如上面为age类型添加了 number类型的约束(数值类型),一旦约定了什么类型,就只能给变量赋值什么类型,否则就会报错

image-20240531072116574

3.2.TS中的原始类型

原始类型:number/string/boolean/null/undefined/symbol

这些类型,完全按照JS中类型的名称来书写即可,非常简单。

/* number/string/boolean/null/undefined/symbol */
let age : number = 30
let username : string = '张三'
let isRunning : boolean = trueconsole.log("年龄:",age)
console.log("姓名:",username)
console.log("是否在奔跑:",isRunning)

image-20240531073429887

3.3.TS中的数组类型

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

对象类型在TS中更加细化,每个具体的对象都有自己类型的语法

数组类型的写法

推荐使用 number[] 这种写法

let numbers : number[] = [1,3,3,4,5,6,7,8,9,10]
let strings : Array<string> = ['1','2','3','4','5','6','7','8','9','10']console.log(numbers)
console.log(strings)

image-20240531074236549

3.4.TS中的联合类型

当数组中既有number类型又有string类型,这个数组的类型该如何书写的?

如果数组中既有number类型 又有string类型,这时候需要使用 | ts中的联合类型(由两个或者多个类型组成类型,表示可以是这些类型中的任意一种),主要这里只是 | 一个竖线,不是两个 两个 || 是逻辑表达式

let listInfo : (string | number | boolean)[] = ['1',2,'3',4,true]// 打印当前集合数据 以及类型
listInfo.forEach(item=>{console.log(item + '\t\t' + typeof(item))
})

image-20240531075119770

3.5.类型别名

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

当一个类型(复杂)并且多次被使用时,可以通过类型别名,简化该类型的使用。

// 类型别名
type CustomArray = (number | string | boolean)[]let user1 : CustomArray = ['张三',21,true]user1.forEach(item=>{console.log(item +"\t" + typeof(item))
})

image-20240603072454983

image-20240603072557432

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

3.6.函数类型

函数的类型实际上指的是:函数的参数函数的返回值 类型

为函数指定类型的两种方式

  1. 单独指定参数、返回值类型。
  2. 同时执行参数、返回值类型。
3.6.1.单独执行参数、返回值类型
// 函数表达式形式
const add = (num1: number, num2: number): number => {return num1 + num2;
}
const res = add(1, 2)
console.log("最终计算结果:" + res + '\t' + typeof (res) + '\t')

image-20240603074952858

image-20240603074402983

3.6.2.同时指定参数,返回值类型

image-20240603075950927

const addNum : (num1:number, num2:number)=> number = (num1,num2)=>{return num1 + num2;
}
const res = addNum(1,3)console.log("最终计算结果:" + res + '\t' + typeof (res) + '\t')

当函数作为表达式时,可以通过 类似箭头函数形式的语法 来为函数添加类型

注意:这种形式只适用于函数表达式

image-20240603075557515

3.6.3.函数的void类型

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

image-20240603081000146

const getUserName = (name: string): void => {console.log(name)
}
getUserName('迪加!')

image-20240603080852241

3.6.4.函数可选参数*

当使用函数实现某个功能时,参数可以传,也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。

比如数组中的 slice方法 可以使用 slice() 也可以使用 slice(1) 也可以使用 slice(1,3)

const mySlice = (start?: number, end?: number): void => {console.log("起始索引:" + start + "\t" + "结束索引:" + end)
}// 使用了可选参数,那么我们自定义的 mySlice()中的参数  可以传 也可以不传了
mySlice()
mySlice(1)
mySlice(1, 3)

image-20240603083421794

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

注意:可选参数只能出现在参数列表的最后,可选参数后面不能再出现参数

3.7.TS中的对象类型

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

对象类型的写法:

// 单行形式
let person2: { name: string; age: number; show(): void; } =
{name: '张三',age: 19,show() {console.log('你好,我是' + this.name + '\t' + '我今年' + this.age + '岁了');}
}
// 多行形式
let person: {name: string,age: number,show(): void
}= {name: '张三',age: 19,show() {console.log('你好,我是' + this.name + '\t' + '我今年' + this.age + '岁了');}
}person.show()
  1. 直接使用{}来描述对象结构,属性采用 属性名:类型 的形式,方法采用 方法名():返回值类型 的形式
  2. 如果方法有参数,就在方法名称后面的小括号中指定类型参数 例如show(name:string):void
  3. 在一行代码中指定对象的多个属性类型时,使用;(分号)来分隔
    1. 如果一行代码只能指定一个属性类型,(通过换行来分割多个属性类型,可以去掉 ;(分号)
    2. 方法的类型可以使用箭头函数的形式(比如{show:()=>void}

image-20240604065526304

TS对象中的可选属性

对象的属性或者方法也是可选的,此时就需要用到可选属性

比如我们在使用axios({ … })时,如果发送GET请求,mothod属性就可以省略

// 如果我们不传methods 那么默认的请求方式就是get
// 可选属性的语法 与 函数的可选参数语法一直 使用 ? 来表示
const myAxios = (config: { url: string, method?: string }): void => {console.log(config);
}myAxios({ url: "http://localhost:9000/api/v1/test" })
myAxios({ url: "http://localhost:9000/api/v1/test", method: 'POST' })

image-20240604071710601

3.8.接口

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

/*** 定义接口*/
interface IPerson {name: string,age: number,printInfo(): void
}let personInfo: IPerson = {name: '张三',age: 22,printInfo() {console.log('姓名:' + this.name + '年龄:' + this.age);}
}personInfo.printInfo()
  1. 使用interface关键字来声明接口
  2. 接口名称,可以是任意合法的变量名称
  3. 声明接口后,直接使用接口名称作为变量的类型
  4. 因为每一行只有一个类型属性,因此属性后面没有 ;(分号)

image-20240605063835210

接口 和 类型别名的对比

  • 相同点:都可以给对象指定类型
  • 不同点:
    1. 接口,只能为对象指定类型
    2. 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
/*** 定义接口*/
interface IPerson {name: string,age: number,printInfo(): void
}/*** 定义类型 注意:这里后面有一个 = 相当于把对象的结构赋值给TPerson*/
type TPerson = {name: string,age: number,printInfo(): void
}type NumStr = number | string

接口继承

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

比如 下面两个接口 都有 x 和 y 两个属性,重复写两次,虽然可以但是很繁琐。

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

更好的方式

interface Point2D {x: number,y: number
}interface Point3D extends Point2D{z: number
}
  1. 使用了 extends(继承)关键字 实现了 Point3D接口继承 Point2D接口
  2. 继承后,Point3D就拥有了Point2D所有的属性和方法
let point_info:Point3D ={x:1,y:2,z:3
}console.log(point_info);

image-20240605070233754

image-20240605070340456

3.9.元组

在地图中,经常使用经纬度坐标来标记位置信息

可以使用数组来记录坐标,那么,该数组的中只有两个元素,并且这两个元素都是数值类型

let position: number[] = [39.3232, 116.1232]

使用number[]的缺点:不够严谨,因为该类型的数组中可以出现任意多个数字。

更好的方法,可以使用元组(Tuple)

元组类型是另一种类型的数组,它准确的直到了包含了多个元素,以及特定索引位置的对应类型

let position: [number, number, boolean] = [39.3232, 116.1232, true]
  1. 元组类型可以确切的标记出有多少个元素,以及每个元素的类型
  2. 该示例中,元素有3个元素,前两个元素的类型是number,第三个元素的类型是boolean

3.10.类型推论

在TS中,某些没有明确指出类型的地方,TS的类型推断机制就会帮助提供类型。

发生类型推论的两种常见场景:

  1. 声明变量并初始化时
  2. 决定函数返回值时
// 声明变量时
let age = 12// 决定函数返回类型时
const addNumer = (num1: number, num2: number) => {return num1 + num2;
}

image-20240605080844673

image-20240605080912374

这两种情况下,类型注解可以进行省略不写!

推荐:能省略的类型注解的地方就省略,充分利用ts类型推论的能力(偷懒)

3.11.函数类型断言

在开发的过程中,有的时候我们比TS更加能明确一个值的类型,可以使用类型断言来指定更具体的类型

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><a href="wwww.baidu.com" id="link">跳转到百度网址</a>
</body>
</html>
<script>const linked = document.getElementById('link')
</script>

image-20240605225236834

注意:getElementById方法返回值的类型时HTMLElement,该类型只包含了所有标签的公共的属性或者方法,不包含a标签特有的href等属性

因此,这个类型不太具体,无法操作href等a标签特有的属性 和 方法

解决方式:这种情况下就需要使用类型断言指定更加具体的类型

as关键字

<script  type="text/typescript">// 如果我们指定的HTMLElement 是无法获取的href等属性的const linked = document.getElementById('link') as HTMLAnchorElement</script>
  1. 使用as关键字实现类型断言
  2. 关键字as后面的类型是一个更加具体的类型,HTMLAnchorElement是HTMLElement的子类型
  3. 通过类型断言,linked的类型变得更加具体,这样就可以访问a标签中的特有的属性和方法

<>第二种方式

这种方式不是很常用,大家作为了解即可,在React中 这种语法格式和jsx冲突 所以用不了的

    // 如果我们指定的HTMLElement 是无法获取的href等属性的const linked = <HTMLAnchorElement>document.getElementById('link')console.log('a标签的值为:',linked)

console.dir($0)

可以打印当前第一个dom元素,并且在列表的最后可以看到该元素的类型

image-20240605231122383

3.12.字面量类型

思考下面代码,两个变量类型分别是什么

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

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

  1. 变量str1的类型为:string
  2. 变量str2的类型为:'Hello TS'

解释:

  1. str1 是一个变量 let ,它的值可以是任意字符串,所以类型为:string
  2. str2 是一个常量 const , 它的值不能变化,只能是’Hello TS’,所以他的类型为:'Hello TS'

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

image-20240606080937567

使用场景:字面量类型配合联合类型一起使用(用来表示一组明确的可选值列表)

比如在贪吃蛇蛇的游戏中,游戏的方向只能是 上、下、左、右,的其中一个。

const changeDirection = (direction: 'up' | 'down' | 'left' | 'right') => {console.log('输入的方向是:', direction);
}changeDirection('up')changeDirection('1')

image-20240606081521146

image-20240606081606589

  1. 参数direction的值只能是 up/down/left/right的中的任意一个
  2. 相对于string 类型,使用字面量类型更加精确严谨。

3.13.枚举

枚举的功能类似于字面量类型+联合类型的组合功能,也可以表示一组明确的可选值

**枚举:定义一组命名常量。**它描述一个值,该值可以是这些命名常量中的一个。

enum Direction { Up, Down, Left, Right }
const changeDirection = (direction: Direction) => {console.log('输入的方向是:', direction);
}
changeDirection(Direction.Up)
  1. 使用enum关键字来定义枚举。
  2. 约定枚举名称、枚举中的值以大写字母开头
  3. 枚举中的多个值通过,(逗号)分隔。
  4. 定义好枚举后,直接使用枚举名称作为类型注解

注意:形参中的direction的枚举类型为Direction,那么实参的值就应该是枚举Direction成员中的任意一个

访问枚举成员:dirction.Up,类似于JS中的对象,直接通过 点(.)语法访问枚举的成员。

枚举成员的值以及数字枚举

当我们把枚举成员作为了函数的实参,那么它的值是什么呢?

image-20240611072802243

当我们把将鼠标放到对应的枚举类型上时,可以看到枚举成员Up的值是为0的。

注意:其实枚举成员是有值的,默认为:从0开始的自增的数值。

我们把枚举成员的值为数字的枚举,称为数字枚举

当前也可以给枚举成员中得到成员初始化值

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

image-20240611073548409

字符串枚举

字符串枚举:枚举的成员的值是字符串

enum DirectionStr { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT' }
console.log(DirectionStr.Left);

image-20240611080952748

注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员都必须有初始值。

3.14.any类型

原则上:不推荐使用any 这个会让TypeScript变为 AnyScript (失去TS类型保护的优势 )

因当值为any类型时,可以对该值进行任意操作,并且不会有代码提示

let obj: any = { x: 0 }obj.bar = 100obj()const n: number = obj

image-20240612082101761

上面的代码操作不会有任何类型的错误提示,即使可能存在报错!

应该尽可能避免使用any,除非临时使用any来避免 书写很长 很复杂的类型

其他隐式具有any类型的情况:

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

注意,这两种情况下都应该去提供类型,防止出现运行时错误

3.15.typeof

在JavaScript中,提供了typeof操作符,用来获取数据的类型

实际上,TS也提供了 typeof操作符:可以在类型上下文中引用变量 或 属性的类型。

使用场景:根据已有变量的值,获取该值的类型来简化书写

// 使用typeof来简化类型的书写let p = { x: 1, y: 2 }
const printPointInfo = (point: { x: number, y: number }) => {console.log('x:', point.x);console.log('y:', point.y);
}
printPointInfo(p)
console.log('----------------------------------------');// 因为 ts 可以根据值 去推断属性的类型  那么可以简化书写
const printPointInfoNew = (point: typeof p) => {console.log('new-x:', point.x);console.log('new-y:', point.y);
}
printPointInfoNew(p)
  1. 使用 typeof 操作符获取变量p的类型,结果 和第一种相同
  2. typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型的上下文
  3. typeof只能用来查询变量或者属性的类型无法查询其他形式的类型(比如函数的调用类型)

image-20240612204343188

4.TS中的高级类型

TS中的高级类型有很多,重点学习以下高级类型

  1. class类
  2. 类型兼容性
  3. 交叉类型
  4. 泛型 和 keyof
  5. 索引签名类型 和 索引查询类型
  6. 映射类型

4.1.class类

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

class的基本使用

class Person {/*** 姓名*/name: string
}const person = new Person()console.log(person);
  1. 根据TS中的类型推断,可以直到Person类的实例对象 person的类型是 Person
  2. TS中的class,不仅提供class的语法功能,也可以作为一种类型的存在

image-20240612205952405

实例属性的初始化

/*** 人*/
class Person {/*** 姓名*/name: string;/** * 性别*/gender = '男生'
}

image-20240612212133462

  1. 声明成员 age,类型为number(没有初始值)
  2. 声明成员 gender,并设置初始值,此时可以省略类型注解(TS类型推论为string类型)

4.2.class的构造函数

class Person {name: stringgender: stringconstructor(name: string, gender: string) {this.name = namethis.gender = gender}
}const person = new Person('张三','女生')
console.log(person.name)
console.log(person.gender)
console.log(person);
  1. 成员初始化(比如age:number)后,才可以通过 this.age访问实例成员。
  2. 需要为构造函数指定类型注解,否则会被隐式推断为any,构造函数不需要返回值类型

image-20240612213447734

image-20240612213542087

image-20240612213055946

4.3.class类的实例方法

class Point{// 这里如果给定默认值 那么就不用写构造方法了x : numbery : numberconstructor(x:number,y:number){this.x = xthis.y = y}/*** 计算方法* @param num  计算的比例数*/scale(num: number):void{this.x *= numthis.y *= numconsole.log(this.x);console.log(this.y);}
}
const p = new Point(1,2)
p.scale(100)
  1. 方法的类型注解(参数和返回值)与函数的用法相同

image-20240612215313841

4.4.class继承

类的继承有两种方式

  • 通过 extends(继承父类)
  • 通过implements(实现接口)

JS中只有 extends,但是TS提供了implements

extends
/*** 动物通用父类*/
class Animal {move() {console.log('Moving along!')}
}/*** 小狗类*/
class Dog extends Animal{bark() {console.log('汪汪!')}
}const dog = new Dog()// 调用父类的移动方法
dog.move()
dog.bark()
  1. 通过extends关键字实现继承
  2. 子类Dog继承父类Animal,则Dog的实例对象dog就同时具有了父类的Animal 和 子类 Dog的所有的属性和方法

image-20240612220041230

implements
interface CommonPrint {/*** 通用输出方法* @param name 输出的信息*/print(name: string): void
}/*** 手机实现类-输出手机信息*/
class PhonePrint implements CommonPrint {print(name: string) {console.log("当前手机的型号为:", name)}
}const phone = new PhonePrint()
phone.print('Iphone 16 Pro Max')
  1. 通过implements关键字让class实现接口
  2. PhonePrint类是实现CommonPrint意味着,PhonePrint类中必须提供CommonPrint接口中指定的所有方法和属性

image-20240612221541300

4.5.类的可见性修饰符

类成员可见性:可以使用TS来控制class的方法 或 属性 对于calss外的代码是否可见

可见性修饰符包括:

  1. public(公有的)
    • public:表示公有的,公开的,公有成员可以被任何地方访问(默认可见性)一般不写就是公有的
  2. protected(受保护的)
  3. private(私有的)、
公有的(public)
class PhonePrint implements CommonPrint {public print(name: string) {console.log("当前手机的型号为:", name)}
}
受保护的(protected)

protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见,对实例对象不可见!不可见!不可见!

class Father{protected money:10000000000
}class Son extends Father{print(){console.log('Father Money:',this.money)}    
}const son = new Son()son.print()// 子类实例对象时不可见的 但是在对应的子类中 是可以访问得到
son.moeny

image-20240612223157930

私有的(private)

私有属性或方法只能在当前类中可见,对其子类和实例对象也是不可见的!

class Father{private money:10000000000
}class Son extends Father{print(){console.log('Father Money:',this.money)}    
}

image-20240612223440383

4.6.readonly只读属性

除了可见性修饰符之外,还有一个比较常见的修饰符号就是 readonly(只读修饰符)

readonly:表示只读,用来防止在构造函数之外对属性进行赋值

class Person {readonly age: numberconstructor(age: number) {this.age = age}
}const person = new Person(12)
person.age = 11// 接口 
interface IPerson{readonly  name : string
}let obj:IPerson = {name : 'Jack'
}obj.name = 'rose'// {} 表示类型
let obj: { readonly name: string } = {name: 'Jack'
}obj.name = 'rose'
  1. 使用readonly关键字修饰的属性是只读的,注意:readonly只能修饰属性,不能修饰方法
  2. 接口或者{}表示的类型,也可以使用readonly

image-20240613071718814

image-20240613072437604

image-20240613072548480

4.7.类型兼容性

两种类型系统:

  1. Structural Type System (结构化类型系统)
  2. Nominal Type System(标明类型系统)

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

class NewPoint {x: numbery: number
}class NewPoint2d {x: numbery: number
}const p1: NewPoint = new NewPoint2d()// 为变量赋值
p1.x = 12
p1.y = 33// 打印当前实例
console.log(p1);
  1. NewPoint NewPoint2d是两个不同名称的类
  2. 变量p1类型被显式标注为NewPoint类型,但是他的确实Point2D的实例,并没有类型的错误
  3. 因为TS是结构化类型系统,只检查NewPoint NewPoint2d结构是否相同,(相同都具有 x 和 y 两个属性,属性类型相同)
  4. 但是 在Nominal Type System中(比如C#,Java中),他们就是不同的类无法兼容

4.8.对象之间的类型兼容性

在结构系统中,如果两个对象具有相同的形状,则认为他们属于同一种类型,这种说法不准确

更准确的说:对于对象类型来说,y的成员至少与x相同,则x兼容y (成员多的可以赋值给少的)

class NewPoint {x: numbery: numberconstructor(x:number,y:number){this.x = xthis.y = y}
}class NewPoint3d {x: numbery: numberz: numberconstructor(x:number,y:number,z:number){this.x = xthis.y = ythis.z = z}
}const p1: NewPoint = new NewPoint3d(1,3,3)// 打印当前实例
console.log(p1)

image-20240613075940827

  1. NewPoint3d 的成员至少 和NewPoint相同,则 NewPoint 兼容 NewPoint3d
  2. 成员多的Point3D可以赋值给成员少的NewPoint

4.9.接口之间的兼容性

除了class之外,TS的其他类型也存在相互兼容的情况,包括

  1. 接口兼容性
  2. 函数兼容性

接口之间的兼容性,类似于class 并且class和interface之间也可以兼容

interface NewPoint {x: number}interface NewPoint2d {x: numbery: number}interface NewPoint3d {x: numbery: numberz: number
}// 类 和 接口之间也是相互兼容的
class  NewPoint4d {x: numbery: numberz: numberconstructor(x:number,y:number,z:number){this.x = xthis.y = ythis.z = z}
}let p99 : NewPoint3d 
let p98 : NewPoint2d 
// 声明p97 类型为 NewPoint
let p97 : NewPoint p97 = new NewPoint4d(1,2,3)console.log(p97);

image-20240614083301914

4.10.函数之间的兼容性

函数之间的兼容性比较复杂,需要考虑

  1. 参数的个数
  2. 参数的类型
  3. 返回值类型
参数个数

参数个数,参数多的个数,可以兼容参数少的个数(参数少的可以赋值给参数多的

// 函数参数的返回类型
let function1 = (a: number): void => { }let function2 = (a: number, b: number): void => { }// 参数少的function1 可以赋值给参数多的function2
function2 = function1// 参数多的不能赋值给参数少的
function1 = function2

image-20240615074824576

// 函数参数的返回类型
type F1 = (a: number) => voidtype F2 = (a: number, b: number) => void// 这里如果不给声明变量赋值初始值会报错
let f1: F1 = (a: number) => { }
let f2: F2 = (a: number, b: number) => { }
f2 = f1console.log(f2);

image-20240615080833783

image-20240615080952827

let arr = ['a', 'b', 'c']
// 上面数组中,示例的类型为:(method) Array<string>.forEach(callbackfn: (value: string, index: number, array: string[]) => void, thisArg ?: any): void
arr.forEach(() => { })
arr.forEach((item) => {console.log(item);
})
arr.forEach((item, index) => {console.log(item, '-', index);
})arr.forEach((item, index, arr) => {console.log(item, '-', index, '-', arr);
})

image-20240615075551398

  1. 参数少的可以赋值给参数多的,所以 function1 和 赋值给 function2
  2. 数组forEach方法的第一个参数是回调函数,类型为 (value: string, index: number, array: string[]) => void
  3. 再JS中省略用不到的函数参数实际上是非常常见的,这种使用方式促成了TS中函数类型之间的兼容性
  4. 并且因为回调函数是有类型的,所以TS会自动推导出 item index array的类型

-**

函数参数

函数参数,相同位置的参数类型要相同(原始类型)或兼容(对象类型)

type F1 = (a: number) => stringtype F2 = (a: number, b: number) => stringlet f1: F1 = (a: number) => ''// 参数少的兼容参数多的,但是参数多的不能兼容参数少的
let f2: F2 = f1console.log('f2的类型是:', f2);

函数类型F2 兼容 函数类型F1 ,因为F2 和 F1的第一个参数相同

image-20240616083608379

返回值

返回值比较简单,我们只关心返回值的类型即可

返回值是原始类型
// 下面这个是错误的一个演示type F1 = (a: number) => numbertype F2 = (a: number, b: number) => stringlet f1: F1 = (a: number) => 0
let f2: F2 = f1// 正确写法
type F1 = (a: number) => stringtype F2 = (a: number, b: number) => stringlet f1: F1 = (a: number) => ''
let f2: F2 = f1

image-20240617075717733

返回值是对象类型
type F1 = () => { name: string }type F2 = () => { name: string, age: number }let f2: F2 = () => { return { name: '张三', age: 123 } }
let f1: F1
f1 = f2console.log('f1',f1);

image-20240617080423790

注意:

  1. 如果返回值类型是原始类型,此时两个类型要相同 比如 f1 和 f2 返回值类型都是string类型
  2. 如果返回值类型是对象类型,此时成员可以赋值给成员的 比如 下面的 f2 和 f1 ,f2的返回值类型 比 f1的返回值类型 多了个age

4.3.交叉类型

**交叉类型(&)**功能类似于接口继承(extends),用来组合多个类型为一个类型(常用于对象)

比如

interface Person {name: string
}
interface Concat {phone: string
}type PersonAndConcat = Person & Concatlet obj: PersonAndConcat = {name: '迪迦',phone: '15256412345'
}
console.log("obj:", obj);

image-20240618072727053

使用交叉类型后,新的类型PersonAndConcat就同时 具备了 PersonConcat的所有属性类型

相当于

type PersonAndConcat = { name: string, phone: string }

4.4.交叉类型 和 接口继承 之间的比对

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

  1. 相同点:都可以实现对象类型的组合
  2. 不同点:两种方式都可以实现类型组合,对于同名属性之间,处理类型冲突的方式不同
// 继承
interface A {print(value: number): string
}
interface B extends A {print(value: string): string
}// 交叉
interface C {print(value: number): string
}
interface D {print(value: string): string
}
type E = A & B// 其实 A & B 可以理解为 print(value : string | number): string

上面代码接口继承的时候会报错(类型不兼容),但是交叉类型没有报错

image-20240618074727897

4.5.*泛型

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

需求:创建一个id函数,传入什么类型数据,就返回该数据本身(参数和返回值类型一致)

// 上面函数 只能接受字符串类型的,如果其他的类型的就不兼容了,
// 可以使用any来处理,但是使用any后,就失去了TS类型保护
const getUserName = (username: string): string => { return username }const getUserName = (username: any): any => { return username }

这个时候就可以使用泛型来处理,泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同类型一起工作,灵活复用。

创建泛型函数
// 第一种写法
const getUserName = <T>(username: T): T => { return username }// 第二种写法
function getUserInfo<T>(value: T): T {return value
}type UserInfo = {username: string
}console.log(getUserName<UserInfo>({ username: '张三' }));

image-20240619065237744

  1. 语法:在函数名称的后面添加<>(尖括号),尖括号中添加类型变量 例如此处的 T
  2. 类型变量 T ,是一种特殊类型的变量,他处理类型,而不是值
  3. 该类型变量相当于一个类型容器,能够捕获到用户提供的类型(具体何种类型,由用户调用该函数时指定)
  4. 因为T是类型,因此可以将其作为函数的参数和返回值类型,表示参数 和返回值具有相同的类型
  5. 类型变量Type,可以是任意合法的变量名称
调用泛型函数
getUserName<UserInfo>({ username: '张三' })

image-20240619065415046

语法:

  1. 语法:在函数名称后面添加 <>(尖括号),尖括号中指定具体的类型,比如 此处的UserInfo
  2. 当传入UserInfo类型后,这个类型就会被函数声明时指定的类型变量 Type捕获到
  3. 次数Type的类型 就是 UserInfo,所以id参数和返回值类型也是UserInfo

这样通过泛型就做到了让 getUserName函数 和 多种不同的类型在一起工作,从而实现了复用的同时保证了类型的安全

简化函数的调用
const getUserName = <T>(username: T): T => { return username }const getId = <T>(id: T): T => { return id }let id = getId<string>('迪迦')// 在调用函数的时候 可以省略 <类型>来简化泛型函数的调用
let id2 = getId('泰罗')
  1. 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用
  2. 此时的TS内部会采用一种叫做 类型参数推断的机制,来根据传入的实参自动推断出类型变量Type的类型

image-20240619071443213

泛型约束

默认情况下,泛型函数的类型变量Type可以代表多个类型,这就导致无法访问任何属性。比如 getId(‘a’)调用函数时获取参数的长度

const getId = <T>(value: T): T => {console.log(value.length);return value;
}function getIdNew<T>(value: T): T {console.log(value.length);return value;
}

image-20240620061152664

T可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length,此时需要为泛型 添加约束来收缩类型(缩窄类型取值范围)

添加泛型约束收缩类型,主要有以下两种方式:

  1. 指定更加具体的类型
  2. 添加约束
指定更加具体的类型

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

const getId = <T>(value: T[]): T[] => {console.log(value.length);return value;
}function getIdNew<T>(value: T[]): T[] {console.log(value.length);return value;
}console.log(getId(['张三','1','2']));
console.log(getId(['李四','3','4','5']));

image-20240620061416499

添加约束
interface ILength {length: number
}
const getId = <T extends ILength>(value: T): T => {console.log(value.length);return value;
}function getIdNew<T extends ILength>(value: T): T {console.log(value.length);return value;
}console.log(getId(['迪加', '1', '2']));
console.log(getId(['赛文', '3', '4', '5']));
  1. 创建描述约束的接口 ILength,该接口中要求提供length属性
  2. 通过extends关键字使用该接口,为泛型(类型变量)添加约束
  3. 该约束表示:传入的类型必须具有length属性

image-20240620063227337

*多个泛型变量情况

泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)

const getPorp = <T, K extends keyof T>(obj: T, key: K) => {console.log('Object:', obj);console.log('Key:', key);return obj[key]
}console.log(getPorp({name: "迪加奥特曼",age: 999999999,addrees: 'null'
}, 'name'));
  1. 添加了第二个类型变量 K,两个类型变量之间使用(,)分隔
  2. keyof 关键字 接受一个对象类型,生成其键名称(可能是字符串或者数字)的联合类型
  3. 上面案例中 keyof T 实际上获取的 是 对象的所有键的联合类型,也就是'name'| 'age' | 'address'
  4. 类型变量 K 受 T 约束,可以理解为:K 只能是 T 所有键中的任意一个,或者只能访问对象中存在的属性

image-20240620065220777

image-20240620071107617

泛型接口

泛型接口:接口也可以配合泛型来使用,增加其灵活性,增强其复用性

interface Book<T> {getBookName: (value: T) => T
}let book: Book<string> = {getBookName(value) {return value;},
}console.log(book.getBookName('Java开发入门!'));
  1. 在接口名称后面添加<类型变量>,那么这个接口就变成了泛型接口。
  2. 接口的类型变量,对接口中所有其他成员可见,也就是,接口中所有成员都可以使用类型变量
  3. 使用泛型接口时,需要显式指定具体的类型,(比如 Book )
  4. 此时,id方法的参数 和返回值类型都 string
数组是泛型接口
// 泛型数组
// 实际上JS中的数组在TS中就是一个泛型接口const strs = ['a', 'b', 'c']const nums = [1, 2, 3, 4, 5]

image-20240624220920081

image-20240624221124673

当我们在使用数组时,TS会根据数组的不同类型,来自动将类型变量设置为相应的类型。

泛型类
class GenericNumber<NumType> {defaultValue: NumTypeadd: (x: NumType, y: NumType) => string = (x, y) => {console.log(x);console.log(y);return 'success';}// 可以省略<>尖括号 当类中提供了constructor 并且提供了属性 那么就不需要在 显式的声明类型了constructor(value: NumType) {this.defaultValue = value}
}const myNum = new GenericNumber(100)// 推荐明确指定泛型类型
const myNum1 = new GenericNumber<number>(100)
myNum1.defaultValue = 10
myNum1.add(1, 2)console.log(myNum1);

image-20240625063312697

  1. 类似于泛型接口,在class名称后面添加**<类型变量>**,这个类就成了泛型类

  2. 此处的add方法,采用的是箭头函数形式的书写方式

  3. const myNum1 = new GenericNumber<number>(100)
    

    类似于泛型接口,在创建class实例时,在类名后面通过**<类型>**来指定明确类型

泛型工具类

TS中内置一些常用的工具类,来简化一些TS中常见的操作

它们都是基于泛型实现的(泛型适用于类型,更加通用),并且时内置的,可以直接在代码中进行使用。

  • Patial
  • Readonly
  • Pick<T,K>
  • Record<K,T>
Patial

用来 创建一个类型,将 泛型T 的所有属性变成可选的。

// 泛型工具类interface Props {id: string,age: number,children: number[]
}// 用来 创建一个类型,将 泛型T 的所有属性变成可选的。
type PartialProps = Partial<Props>

image-20240625064210675

  1. 构造出来的 PartialProps 和 Props 结构相同,但是所有属性都变为可选了
Readonly

用来构造一个类型,将 泛型T的所有属性设置为 readonly(只读)

interface Props {id: string,age: number,children: number[]
}type ReadonlyProps = Readonly<Props>let props: ReadonlyProps = { id: '1', age: 12, children: [1, 2, 3] }props.children = 14

image-20240625064925847

image-20240625064944988

Pick

Pick<T,K> 从T中选择一组属性来构造新的类型

interface Props {id: string,age: number,children: number[]
}type PickProps = Pick<Props, 'id' | 'age'>let pickProps: PickProps = { id: '1212', age: 12 }console.log(pickProps);
  1. Pick工具类中两个类型 :1,表示选择谁的属性 2,表示选择哪几个属性
  2. 第二个参数,如果只选择一个属性,那么写一个参数就行了,多个 就需要使用 | 进行拼接
  3. 第二个参数,传入的参数只能是第一个参数中存在的属性

image-20240625070811282

image-20240625071025891

Record

用来构造一个对象类型,属性键 为K 属性类型为 T

type RecordObj = Record<'a' | 'b' | 'c', string[]>let obj: RecordObj = {a: ['1'],b: ['2'],c: ['3']
}
console.log(obj);

image-20240625071729729

image-20240625071929340

  1. Record工具类型有两个类型变量:
    1. 表示对象有哪些属性
    2. 表示对象属性的类型
  2. 构建的新对象类型RecordObj表示:这个对象的三个属性分别为 a,b,c 属性值的类型都是 string[]

4.6.索引签名类型

在大多数情况下,我们都可以是在使用对象前确定对象的结构,并为对象添加准确的类型。

无法确定对象中有哪些属性时,此时就用到索引签名类型

interface AnyObject {[K: string]: number
}let obj: AnyObject = {a: 1,b: 2
}// 可以指定对应其他类型
interface AnyObject {[K: number]: number
}let obj: AnyObject = {1: 1,2: 2
}
  1. 使用[K: string]来约束接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。
  2. 这样对象中就可以 出现任意多个属性(比如 a,b)
  3. K 只是一个占位符,可以替换成任意合法的变量名称
  4. Js中对象({})的建都是string类型

image-20240625075246371

JS数组中的 也用到索引签名

4.7.映射类型

映射类型:基于旧类型创建新的类型,减少重复提升开发效率

// 这里相当于定义了键
type PrposKeys = 'x' | 'y' | 'z'type TypeDefault = { x: number, y: number, z: number }
type Type = { [Key in PrposKeys]: number }let obj: Type = {x: 1232,y: 213,z: 12132
}console.log(obj);

image-20240626071826806

image-20240626071804128

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

4.8.映射类型(keyof)

映射类型处理根据联合类型创建新类型,还可以根据对象类型来创建

type PrposKeys = { x: number, y: number, z: number, note: string }type PropsKeys2 = { [Key in keyof PrposKeys]: number | string }let obj: PropsKeys2 = {x: 1232,y: 213,z: 12132,note: '测试数据'
}console.log(obj);

image-20240626073645269

image-20240626073721388

  1. 首先,先执行 keyof Props获取到对象类型的Props中所有建的联合类型,x: number, y: number, z: number, note: string

  2. 然后 Key in 就表示 Key 可以是Props中所有的键名称中的任意一个

4.9.索引查询类型

查询单个

刚刚使用的 T[P]语法,在TS中叫做索引查询类型,作用就是:用来查询属性的类型

type Props = { name: string, age: number }type TypeA = Props['age']

image-20240627075243063

  1. 注意:[]中的属性必循存在于被查询的类型中,否则就会报错
查询多个
type Props = { name: string, age: number }// type TypeA = Props['age' | 'name']
type TypeA = Props[keyof Props]

image-20240627075706187

  1. 使用keyof操作符获取Props中所有键对应的类型,结果为:string | number

5.**类型声明文件

在项目开发中,几乎所有的JS应用都会引入许多第三方库来完成任务需求,这些第三方库不管是不是TS编写的,最终都会变成JS代码,才能给开发者使用,我们直到TS提供了类型,才有了代码提示和类型保护等机制。

在项目开发使用第三方库的时候,你会发现他们几乎都有相应的TS类型,这些类型怎么来的呢? 类型声明文件

类型声明文件:用来为已存在的JS库提供类型信息

这样在TS项目中使用这些库时,就会像TS一样,有代码提示、类型保护等机制了。

  • TS的两种文件类型
  • 类型声明文件的使用说明
TS中的两种文件类型
  1. .ts文件
    1. 既可以包含类型信息,又可以包含执行代码
    2. 可以被编译成.js文件,然后执行代码
    3. 用途:编写程序代码的地方
  2. .d.ts文件
    1. 只包含类型信息的类型声明文件
    2. 不会生成.js文件,仅用于提供类型信息
    3. 用途:为js提供类型信息

总结:.ts(代码实现文件) .d.ts(类型声明文件)

类型声明文件的使用说明

在使用TS开发项目时,类型声明文件的使用包括以下两种方式:

  1. 使用已有的类型声明文件
  2. 创建自己的类型声明文件

先学会怎么用别人得到,再去写自己的

内置类型声明文件

TS为JS运行时可用的所有标准化内置了API都提供了声明文件,比如我们在使用数组的时候,数组的所有方法都会有相应的代码提示以及类型信息

image-20240628071501945

image-20240628071708896

实际上这些都是TS提供的内置类型声明文件,可以通过Ctrl + 鼠标左键 来查看内置类型声明文件

第三方库的类型声明文件
目前,几乎所有常用的第三方库都有相应的类型声明文件

第三方库的类型声明文件主要有两种形式:

  • 库自带类型的声明文件
  • 有DefinitelyTyped提供

库自带的类型声明文件,例如axios

image-20240628072937625

这种情况下,正常导入该库,TS就会自动加载库自己的类型声明文件,用来提供该库的类型声明。

由DefinitelyTyped提供

DefinitelyTyped是一个github仓库,用来提供高质量的TypeScript类型声明

可以通过npm来下载该仓库提供的TS类型声明包,这些包的名称格式为: @types/*

比如下载 @types/react

可以尝试使用

npm i --save-dev @types/react(库的一些名称)

image-20240628073532219

安装后,TS会自动加载该类声明包,以提供该库的类型声明。

image-20240628073912215

5.1.创建自己的类型声明文件

项目内共享类型

项目内共享类型:如果多个 .ts文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享。

操作步骤:

  1. 创建 user.d.ts 类型声明文件

    1. // user.d.ts
      export type UserInfo = {username: string,password: string
      }
  2. 创建需要共享的类型,并使用export导出(TS中中的类型也可以使用 import/export 实现模块化功能)

  3. 在需要使用共享类型的 .ts 文件中,通过import 导入即可(.d.ts后缀导入时,直接忽略即可)

    1. import { UserInfo } from "../types/user";let user: UserInfo = {username:'137230256',password:'123456@111'
      }console.log('user:[',user,']');

image-20240628214532465

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

什么时候需要为已有JS文件提供类型声明呢?

  1. 在将JS项目迁移到 TS项目的时候,为了让已有的 .js 文件有类型声明
  2. 成为库作者,创建库给其他人使用

注意:类型声明文件的编写 与 模块化的方式相关,不同的模块化方式有不同的写法。

演示:基于最新的 ESModule(import/export)来为已有的.js文件,创建类型声明文件

开发环境准备:使用webpack搭建,通过 ts-loader处理 .ts 文件

说明:TS文件中也可也使用 .js 文件,到导入 .js 文件时,TS会自动加载 和 .js同名的 .d.ts文件,以提供类型声明

declare关键字

用于类型声明,为其他地方(比如,.js 文件)已经存在的变量 声明类型,而不时创建一个新的变量

  1. 对于 type interface 等这些明确就是 TS类型的(只能在TS中使用的),可以省略 declare关键字
  2. 对于 let funcation 等具有双重含义的,(在JS 和 TS 中都可以使用),应该使用 declare关键字,明确指定此处用于类型声明

1.创建JS文件

user.js

export const getUserInfo = (params)=>{console.log('获取用户信息成功!');console.log('params:',params);
}

2.创建 .d.ts文件

user.d.ts

// 为方法的请求参数添加类型
declare type UserInfo = {username: string,password: string
}// 为js文件 的方法添加类型
declare function getUserInfo(params:UserInfo):void// 导出JS文件
export { getUserInfo }
// 或者 
module.exports = { getUserInfo };

3.测试

import { getUserInfo, UserInfoParams } from "./user";// 调用获取用户信息方法
let params: UserInfoParams = {username: '1232',password: '2132434'
}console.log(params);console.log('------------------------------------');getUserInfo(params)

image-20240629082447543

image-20240629082355708

如果无法使用 export 导出 js方法 那么可以换一种思路

package.json

{"compilerOptions": {"module": "commonjs"}
}

user.js

function getUserInfo(params) {console.log('获取用户信息成功!');console.log('params:', params);
}module.exports = { getUserInfo };

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

广东省钟表行业协会第十二届会员大会暨2024年钟表行业发展交流会

6月25日广东省钟表行业协会第十二届会员大会暨2024年钟表行业发展交流会在广州万富希尔顿酒店隆重召开。大会选举沙胜昔为广东省钟表行业协会第十二届理事会会长。 领导发言 新任会长 沙胜昔 首席荣誉会长 吴伟阳 新老会长交接仪式 本次大会&#xff0c;全国钟表大伽齐参与…

前端Web开发HTML5+CSS3+移动web视频教程 Day3 CSS 第1天

P29 - P43 从此开始进入 CSS 的学习。前面都是 HTML 的学习。 CSS 的作用&#xff1a;美化。 HTML 只是规定了网页内容有哪些&#xff0c;在网页中显示的位置默认是从上到下显示&#xff0c;还带有默认效果&#xff0c;比如超链接有颜色有下划线&#xff0c;无序列表有小圆点…

CSS Flex弹性布局

一、传统布局与flex布局 1、传统布局 2、flex布局 二、flex布局原理 1、布局原理 2、flex布局体验 三、flex布局父项常见属性 1、常见的父项属性 2、flex-direction设置主轴的方向 3、justify-content 设置主轴上的子元素排列方式 4、flex-wrap 设置子元素是否换行 …

二叉树从根节点出发的所有路径

二叉树从根节点出发的所有路径 看上图中 二叉树结构 从根节点出发的所有路径 如下 6->4->2->1 6->4->2->3 6->4->5 6->8->7 6->8->9 逻辑思路&#xff1a; 按照先序遍历 加 回溯法 实现 代码如下 // 调用此方法&#xff0c;将根节点传递…

上位机图像处理和嵌入式模块部署(mcu 项目1:上位机编写)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面&#xff0c;我们说过要做一个报警器。如果只是简单做一个报警器呢&#xff0c;这个基本上没有什么难度。这里&#xff0c;我们就适当提高一下…

Navicat上新啦

前言 Navicat&#xff0c;在数据库界&#xff0c;几乎是一个神奇的存在&#xff0c;似乎统治了数据库开发工具的“一片天”。且看下图&#xff1a; 红的蓝的绿的橙的…&#xff0c;可以说&#xff0c;留给它的color不多了。 那么商业BI到服务监控、从云托管到云协作&#xff…

短剧App开发的全攻略

短剧App开发的全攻略可以概括为以下几个关键步骤&#xff1a; 1、市场调研与需求分析 进行市场调研&#xff0c;研究目标用户群体&#xff0c;了解他们的需求和偏好。 观察竞争对手的App&#xff0c;分析他们的优点和缺点&#xff0c;以此为基础来制定自己的开发计划。 确定App…

Zookeeper节点ACL权限设置—digest模式

ACL全称为Access Control List&#xff08;访问控制列表&#xff09;&#xff0c;用于控制资源的访问权限。ZooKeeper使用ACL来控制对其znode&#xff08;ZooKeeper数据树的数据节点&#xff09;的访问。 zk利用ACL策略控制节点的访问权限: CREATE c 可以创建子节点 DELETE …

3D打印随形水路在注塑生产中的显著优势

3D打印技术在模具制造中已崭露头角&#xff0c;特别是在注塑生产中&#xff0c;3D打印随形水路的应用正变得日益普遍。 首先&#xff0c;该技术能精准优化模具温度。3D打印随形水路随形冷却的设计让模具温度更加均匀&#xff0c;水路更贴近产品&#xff0c;有效提升产品品质和缩…

k8s如何使用 HPA 实现自动扩展

使用Horizontal Pod Autoscaler (HPA) 实验目标&#xff1a; 学习如何使用 HPA 实现自动扩展。 实验步骤&#xff1a; 创建一个 Deployment&#xff0c;并设置 CPU 或内存的资源请求。创建一个 HPA&#xff0c;设置扩展策略。生成负载&#xff0c;观察 HPA 如何自动扩展 Pod…

redis实战-缓存穿透问题及解决方案

定义理解 缓存穿透&#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远都不会生效&#xff08;只有数据库查到了&#xff0c;才会让redis缓存&#xff0c;但现在的问题是查不到&#xff09;&#xff0c;会频繁的去访问数据库。 解决…

系统运维面试总结(系统权限)

系统运维面试总结&#xff08;系统权限&#xff09; 一、权限优化简述Linux权限划分原则二、备份策略三、Raid四、资源查看五、Linux启动流程 一、权限优化简述Linux权限划分原则 ckhunter也是一款常用的Linux杀毒软件 不可修改但可删除 二、备份策略 供参考较为全面的备份方案…

【Redis】SpringBoot连接Redis

1. 创建项目并配置文件 勾选NoSQL中的 Spring Data Redis。当然,把 Web 中的 SpringWeb 也勾选一下.方便写接口进行后续测试。 在 application.yml 中配置 2. 不同数据类型使用Demo 在SpringBoot中&#xff0c;为我们提供了StringRedisTemplate类&#xff0c;供我们处理一些文…

期末重现题型--错题集

看书里的定义&#xff1a;链表是一种常见而重要的动态存储分布的数据结构。它由若干个同一结构类型的“结点”依次串联而成的。

keil软件的一些使用技巧

1.MDK 的 TAB 键支持块操作 也就是可以让一片代码整体右移固定的几个位&#xff0c;也可以通过 SHIFTTAB 键整体左移固定的几个位。 2.快速注释与快速消注释 就是先选中你要注释的代码区&#xff0c;然后右键&#xff0c;选择Advanced→Comment Selection 就可以了。 3.快速打…

ComfyUI效率节点Efficient示例

文生图工作流 Efficient Loader节点用于高效加载和缓存模型 ckpt_name&#xff1a;选择要加载的检查点模型的名称。通常选择你的主要模型名称 vae_name&#xff1a;定义要使用的VAE&#xff08;变分自编码器&#xff09;模型。一般选择与你的主要模型匹配的VAE&#xff0c;或者…

Open vSwitch 行为匹配的实现

一、Datapath 模块的行为匹配 在 Open vSwitch 的数据包转发流程中&#xff0c;存在快速路径和慢速路径两种模式&#xff0c;如下图所示&#xff1a; 其中&#xff0c;快速路径直接在 Datapath 模块完成行为匹配&#xff0c;将数据包转发出去。而慢速路径的数据包无法在 Datapa…

厦门新能安科技Ampace校招实习待遇及Verify测评SHL演绎数字推理历年真题题库

一、厦门新能安科技公司介绍 厦门新能安科技有限公司主要业务包括电池制造和销售&#xff0c;电容器及其配套设备制造与销售&#xff0c;电池零配件生产与销售。此外&#xff0c;公司还提供包括技术服务、技术开发、技术咨询、技术交流、技术转让和技术推广在内的全方位服务。公…

HarmonyOS Next开发学习手册——选项卡 (Tabs)

当页面信息较多时&#xff0c;为了让用户能够聚焦于当前显示的内容&#xff0c;需要对页面内容进行分类&#xff0c;提高页面空间利用率。 Tabs 组件可以在一个页面内快速实现视图内容的切换&#xff0c;一方面提升查找信息的效率&#xff0c;另一方面精简用户单次获取到的信息…

微软推出集成GPT-4o的文本转语音虚拟数字人服务

微软近日宣布&#xff0c;其全新的文本转语音虚拟数字人服务正式上线&#xff0c;并集成了GPT-4o技术。这一服务为用户提供了创建实时互动数字人的可能。通过先进的自然语言处理技术&#xff0c;数字人能够将文本转化为自然流畅的语音&#xff0c;并配以生动的虚拟形象&#xf…