【TypeScript】TS入门到实战(详解:高级类型)

目录

第三章、TypeScript的数据类型

3.1 TypeScript的高级类型

3.1.1 class

3.1.1.1 熟悉class类

3.1.1.2 class类继承的两种方式

3.1.1.3 class类的5种修饰符

3.1.2 类型兼容

3.1.3 交叉类型

3.1.4 泛型

3.1.4.1 创建泛型函数

3.1.4.2 泛型函数的调用

3.1.4.3 泛型约束

3.1.4.4 泛型接口

3.1.4.5 泛型类

3.1.4.6 泛型工具类型

3.1.5 索引签名类型

3.1.6  映射类型


第三章、TypeScript的数据类型

3.1 TypeScript的高级类型

3.1.1 class

3.1.1.1 熟悉class类
  • 引言
class Person {}
const p = new Person()

当创建一个Person类时,添加了实例对象p,由类型推论可以知道它的类型是Person;说明TS 中的 class,不仅提供了 class 的语法功能,也作为一种类型存在。接下来跟着小编一起理解class吧。

  • 初始化属性
class Person {
    age: number // 适用没有默认值
    name = 'VE' // 适用于有默认值
}
  1. 声明成员 age,类型为 number(没有初始值)。
  2. 声明成员 name,并设置初始值,此时,可省略类型注解(TS 类型推论 为 string 类型)。
  3. 当没有默认赋值时,需要自定义属性的类型,否则会默认为any类型赋值后,属性的类型会与赋值的类型一致,赋值的类型ts会类型推论
  4. 看下图的几种情况:

 

  • 构造函数
class Person {
    age: number
    name: string

    constructor (name: string, age: number){ // 初始化实例对象使用的,写了contructor函数后初始化实例对象时也需要传参从而初始化实例中的属性
        this.age = age
        this.name = name
    }
}
const p = new Person('10', 10)
  1. 只有初始化(比如,age: number)后,才可以通过 this.age 来访问实例成员。
  2. 需要为构造函数指定类型注解,否则会被隐式推断为 any;构造函数不需要返回值类型。
  3. constructor构造函数是初始化实例对象使用的,写了contructor函数后初始化实例对象时也需要通过传参从而初始化实例中的属性参数顺序跟constructor一致

 

  • 实例方法
class Point {
    x: number
    y: number

    scale (n: number): void{
        this.x *= n
        this.y *= n
    }
}
  1. 方法初始化,它的的类型注解(参数和返回值)与函数用法相同

3.1.1.2 class类继承的两种方式

类继承的两种方式:1、extends(继承父类) 2、implements(实现接口)
注意:JS 中只有 extends,而 implements 是 TS 提供的。

  • extends(继承父类)
class Animal { // 这是父类Animal 
    distance: string = '10' // 父类中的属性
    move () { // 父类中的方法
        console.log('move along')
    }
}

class Dog extends Animal { // 子类 Dog 继承父类 Anima
    bark () { // 子类自身的方法
        console.log('汪汪汪~')
    }
}

const dog = new Dog() // dog是Dog的实例对象,

// 从而dog同时具有了父类 Animal 和 子类 Dog 的所有属性和方法
dog.move()
dog.bark()
dog.distance = '20'
console.log('距离', dog.distance)

  1. 通过 extends 关键字实现继承。
  2. 子类 Dog 继承父类 Animal,则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。
  • implements(实现接口): 这是ts新增的,顾名思义就是对一个接口的实现

interface Singable { // 这是添加的一个接口,定义了Singable有哪些属性和方法的类型
    name: string 
    sing: () => void
    toSing(): void
}

class Person implements Singable { // Person类是对Singable接口的实现
    name = 'VE'
    sing = () => { // 函数方面是箭头函数还是普通函数需要我们自己规范
        console.log('你是我的小啊小苹果')
    }
    toSing(): void {
        console.log('去ktv唱歌')
    }
}

const p = new Person() // p是Person的一个实例对象

// 从而拥有了Singable 接口 指定的所有方法和属性
console.log(p.name)
p.sing()
p.toSing()

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

可见性修饰符包括:1、public(公有的) 2、protected(受保护的) 3、private(私有的)4、readonly(只读修饰符)5、static(静态的)

  • public(公有的)

class Animal {
    public move () { // 在父类的方法中添加pubilc(默认,可省略)
        console.log('move along')
    }
}
class Dog extends Animal { // 子类
    bark () {
        console.log('汪汪汪~')
    }
}

class Cat extends Animal { // 子类
    bark () {
        console.log('喵~喵~喵~')
    }
}

const dog = new Dog() // 由于父类设置的为pubilc修饰符,所有其子类实例可以调用父类的方法

dog.move()
dog.bark()

const cat = new Cat() // 由于父类设置的为pubilc修饰符,所有其子类实例可以调用父类的方法

cat.move()
cat.bark()

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

class Animal {
    protected move () { // 将方法添加protected关键字设置成受保护的,
        console.log('move along')
    }
    toMove () {
        this.move() // 在父类本身中只能通过this访问受保护的属性/方法
    }
}
class Dog extends Animal {
    bark () {
        console.log('汪汪汪~')
        this.move() // 在子类中也只能通过this访问受保护的属性/方法
    }
}
// const animal = new Animal() // 但是他们的实例不能访问到受保护的属性和方法
// animal.move() // 本身实例调用会报错属性move受保护,只能在类“Animal”及其子类中访问

const dog = new Dog()

// dog.move() // 子类调用会报错属性move受保护,只能在类“Animal”及其子类中访问
dog.bark() // 可以通过提供方法添加新的方法从而间接访问
console.log('=====')
dog.toMove()

  1. 在类属性或方法前面添加 protected 关键字,来修饰该属性或方法是受保护的
  2. 在子类的方法内部可以通过 this 来访问父类中受保护的成员,但是,对其声明所在类和子类不可见!
  • private(私有的)

class Animal {
    private move () {
        console.log('move along') // 将方法添加private 关键字设置成私有的,
    }
    toMove () {
        this.move() // 只能在当前类中访问
    }
}
class Dog extends Animal {
    bark () {
        console.log('汪汪汪~')
        // this.move() // 在子类的方法中也不能访问
    }
}
// const animal = new Animal()
// animal.move() // 本身实例调用会报错属性move斯私有属性,只能在类“Animal”中访问

const dog = new Dog()

// dog.move() // 子类调用会报错属性move为私用属性,只能在类“Animal”中访问
dog.bark()
console.log('=====')
dog.toMove()  // 但是子类可以通过调用父类提供的方法从而间接的访问父类的私有属性

 

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

class Animal {
    readonly species: string = '爬行动物' // 修饰属性为只读是, 仅可在构造函数中修改
    move () {
        // this.species = '飞行动物' // 报错无法赋值,species为只读属性
        console.log('move along')
    }
    constructor(species: string){
        this.species = species
    }
}
class Dog extends Animal {
    bark () {
        console.log('汪汪汪~')
    }
}

const dog = new Dog('爬行')

dog.bark()
console.log('dog', dog.species)

 

  1. 使用 readonly 关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法。
  2. 注意:只要是readonly来修是属性,必须手动提供明确的类型,属性 species 后面的类型注解(比如,此处的 string)如果不加,则 species 的类型为 爬行动物 (字面量类型)。
  3. 仅可在构造函数中修改(也就是初始化时)
  4. 接口或者 {} 表示的对象类型,也可以使用 readonly
// 接口或者 {} 表示的对象类型,也可以使用 readonly
interface IPerson {
    readonly name: string
}

let p: IPerson = {
    name: 'VE'
}

let per: { readonly name: string, age: number } = {
    name: 'VE',
    age: 18
}

// p.name = 'LO' // 如果没加readonly是可以修改的,但是加了之后会报错为只读属性,无法赋值
// per.name = 'LO'
per.age = 20
  • static(静态的)
class Animal {
    // 普通属性
    name: string
    // 静态属性
    static gander: string = '女'
  
    // 构造函数前不能添加 static
    constructor (name: string) {
        this.name = name
  
        // 访问静态属性,需要通过 【类名.属性】访问
        console.log(this.name, Animal.gander)
    }
  
    // 普通方法
    sayHi () {
        console.log('hello你们好')
    }
  
    // 静态方法
    static sayStatic () {
        console.log(Animal.gander)
    }
}
// 实例化对象
const per = new Animal('花花')
// 访问普通方法,通过实例
per.sayHi()
// 访问静态方法,要通过 【类名.方法】访问
Animal.sayStatic()

  1. 使用 static 关键字修饰该属性/方法是静态的
  2. 静态成员 在使用时通过 类名.静态成员 这种语法来调用的,而不是通过 this实例

3.1.2 类型兼容

  • 目前有两种类型系统:1 Structural Type System(结构化类型系统) 2 Nominal Type System(标明类型系统)
  • TS 采用的是结构化类型系统,也叫做 duck typing(鸭子类型),类型检查关注的是值所具有的形状。也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。(可以这么理解但是,该说法并不准确)。用于确定一个类型是否能赋值给其他类型
  • 对于对象类型来说,y 的成员至少与 x 相同,则 x 兼容 y(成员多的可以赋值给少的)
  • 例子1:
class Point {number1 = 10; number2 = 9}
class Point2D {number1 = 16; number2 = 12}

const p: Point = new Point2D()
console.log('p', p.number1, p.number2)

  1. Point 和 Point2D 是两个名称不同的类。
  2. 变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。
  3. 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(结果是相同,都具有 x 和 y 两个属性,属性类型也相同)。
  4. 但是,如果在 Nominal Type System(标明类型系统) 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。
  5. 这里我们可以理解p被类型标注了Point的number1与number2,分别都是number类型,最后被赋值了Point2D
  • 例子2:兼容可以立即成向下兼容,在class中定义:成员多的可以赋值给成员少的(理解,左边的参数必须都有,而右边赋值的则是至少需要包含左边参数)
class Point {number1 = 10; number2 = 9}
class Point2D {number1 = 16; number2 = 12}
class Point3D {number1 = 17; number2= 1; numbe3 = 20 }
const p: Point = new Point2D()
const p1: Point2D = new Point3D() // 成员多的 Point3D 可以赋值给成员少的Point2D / Point
console.log('p', p.number1, p.number2)

…… 

3.1.3 交叉类型

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

interface Person { // 第一个接口定义了一部分类型
    name: string
}
interface Contact { // 第二个接口定义了不部分类型
    phone: string
}

type PersonDetail = Person & Contact // 定义新的类型利用&交叉类型合并, 注hengsm意新的类型用tpye类型别名声明的

let person: PersonDetail = { // 新的类型同时拥有上面的两种声明类型
    name: 'zs',
    phone: '177 5490 0987'
}
  1. 使用交叉类型后,新的类型 PersonDetail 就同时具备了 Person 和 Contact 的所有属性类型。
  2.     相当于type PersonDetail = { name: string, phone: string }
  • 交叉类型(&)和接口继承(extends)的对比

-- 使用extends

interface A {
    name: string
    fn: (number: number) => void
}
interface B extends A {
    gander: string
    //  fn: (number: string) => void
}
const obj: B = {
    name: 'VE',
    fn: (number: number) => {},
    gander: '女'
}

-- 使用交叉类型

interface A {
    name: string
    fn: (number: number) => number
}
interface B {
    // name: number
    fn: (number: string) => string
}
type C = B & A
const obj: C = {
    // name,
    name: '',
    fn: (number) => {
        return number
    }
}

-- 相同点:都可以实现对象类型的组合。
-- 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
       extends 不支持相同属性/方法的继承,会报错类型不兼容
       & 支持相同属性/方法的继承可以理解成利用联合类型 & 连接,两种类型同时具备才可,否则会报错,不可分配

3.1.4 泛型

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

3.1.4.1 创建泛型函数
  • 引入思考:目前有一个需求,创建一个 id 函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)

// 实现方法一:手动限制一个类型,缺点也很明显,我们只能传number数据类型,传其他数据类型时会报错
function id_first(value: number) {
    return value
}
// 实现方法二:不限制数据类型,那么ts本身类型推断,则会将传参推断为any,失去了ts的意义
function id_second(value) {
    return value
}
  •  实现方法:创建泛型函数,泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用。
// 实现方法:创建泛型函数,泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用。
function id<Type>(value: Type): Type {
    return value
}

  1.  语法:在函数名称的后面添加 <>(尖括号)尖括号中添加类型变量(可自定义语义化名称),比如此处的 Type。
  2. 该函数中类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。
  3.  该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。
  4.  因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
  5. 类型变量 Type,可以是任意合法的变量名称
3.1.4.2 泛型函数的调用
  • 正常调用 
function id<Type>(value: Type): Type {
    return value
}

// 调用泛型函数:既要传类型,也要传参数
const num = id<number>(1)
const str = id<string>('10')
const arr = id<(number | string)[]>(['10', '1', 10])

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

// <类型> 可以不写
let sp_num = id(10)
let sp_str = id('10')
let sp_arr = id(['10', '1', 10])

主要注意:这种方法是利用类型推断实现的,直接传参要与前面带类型的要区分 

  1. 例如sp_num、sp_str变量,我们能发现函数被推断成id<10>(value: 10): 10 字面量类型,最终返回数字10才使得sp_num推断成number类型
  2. 例如sp_arr变量,函数被推断<(string | number)[]>(value: (string | number)[]): (string | number)[],最终返回结果['10', '1', 10]使得sp_arr推断成(string | number)[]类型
  3.  如果我们期望第1点最终是number类型而不是字面量类型,但是类型推断又是给我们推断成字面量类型,我们还是需要传类型的:id<number>(10)
3.1.4.3 泛型约束
  • 默认情况下泛型函数的类型变量 Type 可以代表多个类型,这导致无法访问任何属性,如下:

function id_view<Type>(value: Type): Type {
    console.log('长度', value.length) // 报错原因:Type 可以代表任意类型,无法保证一定存在 length 属性,比如 number 类型没有 length。
    return value
}

  • 于是,就需要为泛型添加约束来收缩类型(缩窄类型取值范围),添加泛型约束收缩类型,主要有以下两种方式:1、指定更加具体的类型 2、添加约束

  • 1、指定更加具体的类型

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

function id_view<Type>(value: Type[]): Type[] { // 将类型修改为 Type[](Type 类型的数组),因为只要是数组就一定存在 length 属性,因此就可以访问了
    console.log('长度', value.length)
    return value
}

id_view([1, 2])
id_view<number | string>([1, '2'])
id_view<number[]>([1, 2]) // 注意添加<类型>是的写法,由于我们已经在类型中写了Type[],所以使用时不需要再加[]

注意书写:(类型可不传,ts会通过类型断言得到)

 报错:

  • 2、添加约束 
interface ILength { length: number } // 创建描述约束的接口 ILength,提供length属性


function id_view<Type extends ILength>(value: Type): Type { // 对Type添加约束,通过 extends 关键字使用ILength接口
    console.log('长度', value.length)
    return value
}

// 使用类型约束之后我们在调用时就必须需要传具有 length 属性的类型(string/array…),否则调用会报错
// id_view<number>(10) // 会报错number类型不满足约束的ILength
id_view('10')
id_view<string>('10')
// id_view<{name: string, age: number}>({name: 'VE', age: 23}) // 会报错

  1. 创建描述约束的接口 ILength,该接口要求提供 length 属性
  2. 通过 extends 关键字使用该接口,为泛型(类型变量)添加约束。
  3. 该约束表示:传入的类型必须具有 length 属性
  • 泛型的类型变量可以有多个,并且类型变量之间还可以约束

function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) { // keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
    return obj[key]
}
let person = {name: 'VE', age: 23}
const personName = getProp(person , 'name')
console.log('personName', personName)

  1. 添加了第二个类型变量 Key,两个类型变量之间使用(,)逗号分隔。
  2.  keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
  3. 本示例中 keyof Type 实际上获取的是 person 对象所有键的联合类型,也就是:'name' | 'age'。
  4. 类型变量 Key 受 Type 约束,可以理解为:Key 只能是 Type 所有键中的任意一个,或者说只能访问对象中存在的属性

3.1.4.4 泛型接口
  • 接口配合泛型使用,以增加其灵活性,复用性

interface idFunc<Type> {
    id: (vlue: Type) => Type
    ids: () => Type[]
}
const obj: idFunc<number> = { // 使用泛型接口时,必须需要显式指定具体的类型
    id(num) {
        return num
    },
    ids() {
        return [1, 2]
    }
}

  1. 语法:在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口。
  2. 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量
  3. 使用泛型接口时,需要显式指定具体的类型(比如,此处的 IdFunc<number>)。
  4. 此时,id 方法的参数和返回值类型都是 number;ids 方法的返回值类型是 number[]。
  • JS 中的数组在 TS 中就是一个泛型接口:当我们在使用数组的方法时,TS 会根据数组的不同类型,来自动将类型变量设置为相应的类型,如下:

const strsArr = ['a', 'b', 'c']
const arr1 = [1, 2, 'a']
strsArr.sort()
arr1.sort()
arr1.forEach(()=>{})

 

3.1.4.5 泛型类
  • 使用class类 配合泛型来使用

  • 创建泛型类并使用

关于创造泛型类涉及到的报错:

【TypeScript 类报错】Property ‘name‘ has no initializer and is not definitely assigned in the constructor-CSDN博客

class GenericNumber<NumType> { // 创建泛型类
    defaultValue: NumType
    add?: ( x: NumType, y: NumType ) => NumType
    
    constructor(defaultValue: NumType) { // 创建构造函数
        this.defaultValue = defaultValue
    }
}
const myNum = new GenericNumber<number>(10) // 类似于泛型接口,在创建 class 实例时,在类名后面通过 <类型> 来指定明确的类型。
myNum.defaultValue = 10
// 初始化方法
myNum.add = function(a, b) {
    return a + b
}
// 之后调用方法
const value = myNum.add(1, 2)
console.log(myNum.defaultValue, value)

(针对于泛型类里的方法创造了实例之后一定要先初始化再调用)

3.1.4.6 泛型工具类型
  • TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作,它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。

  • 常用的:1、Partial<Type>  2、 Readonly<Type> 3、 Pick<Type, Keys> 4 、Record<Keys, Type>

  • 1、Partial<Type>:用来构造(创建)一个类型,将 Type 的所有属性设置为可选
interface Props {
    id: string
    children: number[]
}

// 构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。鼠标悬浮在PartialProps上可以发现PartialProps的属性都添加了?:表示可有可无
type PartialProps = Partial<Props> 
let partialVal: PartialProps = {} // 添加Partial后,参数可写可不写
let noPartialVal: Props = { // 未添加Partial,使用该类型字段必须都传
    id: '1',
    children: [1, 2]
}

  • 2、Readonly<Type> 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)
interface Props {
    id: string
    children: number[]
}

type readonlyProps = Readonly<Props> // 会给每一个类型前面添加一个 readonly 修饰
let readonVal: readonlyProps = { // 一旦定义里面的属性就无法修改
    id: '001',
    children: [1, 2, 3]
}
// readonVal.id = '002' // 无法修改,报错无法赋值,为只读属性

  • 3、Pick<Type, Keys> 从 Type 中选择一组已有的属性来构造新类型
interface Props {
    id: string
    children: number[]
}

type PickProps = Pick<Props, 'id'>
let PickVal: PickProps = {
    id: '',
}
// 如果传多个使用 | 连接,选择的不能是类型中没有的属性
type PickProps = Pick<Props, 'id' | 'children'> 
let PickVal: PickProps = {
    id: '',
    children: [1, 2, 3]
}
  • 4、Record<Keys,Type> 构造一个对象类型,属性键为 Keys ,使用 | 连接多个属性,属性类型为 Type。
interface Props {
    id: string
    children: number[]
}

type RecordProps = Record<'id', Props>
let RecordVal: RecordProps = {
    id: {
        id: '',
        children: [1, 2]
    }
}
type RecordProps2 = Record<'id' | 'title', string>
let RecordVal2: RecordProps2 = {
    id: '',
    title: ''
}

3.1.5 索引签名类型

  • 绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。
  • 使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了
interface AnyObject {
    [key: string]: number
}
let obj: AnyObject = {
    a: 1,
    b: 22,
    1: 2
}

  1. 使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称,都可以出现在对象中
  2.  这样,对象 obj 中就可以出现任意多个属性(比如,a、b 等)。
  3. key 只是一个占位符,可以换成任意合法的变量名称。
  4. 隐藏的前置知识:JS 中对象({})的键是 string 类型的。
interface MyArray<T> {
    [key: number]: T // 索引都是数字,数组符合要求,T规定类型
}
let arr: MyArray<number> = [1, 2, 3]
  1. 在 JS 中数组是一类特殊的对象,特殊在数组的键(索引)是数值类型。
  2. 并且,数组也可以出现任意多个元素。所以,在数组对应的泛型接口中,也用到了索引签名类型

3.1.6  映射类型

小编重开一篇文章单独讲。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++纵横字谜

1.实现一个纵横字谜 2.支持14x14的网格 3.可以查看答案 4.猜测错误会提示答案信息 5.从txt读取词汇 6.每次游戏开始 随机生成纵横字谜 n’h

Jest是什么软件?

Jest是一个由Facebook开发的开源JavaScript测试框架&#xff0c;它专为JavaScript项目的测试而设计&#xff0c;特别适用于React和Node.js环境。Jest以其简单的配置、高效的性能和易用性而闻名&#xff0c;成为现代JavaScript项目中不可或缺的测试工具。以下是关于Jest的详细解…

Android Compose 十二:常用组件列表 上拉加载

列表 上拉加载 当前思路 判断 列表最后一个显示的条目 为 数据集合的长度-1 用来记录刷新状态 var refreshing by remember {mutableStateOf(false)}数据集合 val list remember{List(10){"条目》》${it}"}.toMutableStateList()}用来记录列表当前状态及状态变化…

探讨4层代理和7层代理行为以及如何获取真实客户端IP

准备工作 实验环境 IP角色192.168.1.100客户端请求IP192.168.1.100python 启动的HTTP服务192.168.1.102nginx服务192.168.1.103haproxy 服务 HTTP服务 这是一个简单的HTTP服务&#xff0c;主要打印HTTP报文用于分析客户端IP #!/usr/bin/env python # coding: utf-8import …

地理信息科学:生态保护的智慧经纬

在地球这颗蓝色星球上&#xff0c;每一片森林的呼吸、每一条河流的流淌&#xff0c;都是生命交响曲中不可或缺的音符。而地理信息科学&#xff08;GIS&#xff09;&#xff0c;正是我们手中解读自然密码、护航生态平衡的精密仪器。今天&#xff0c;让我们深入探讨GIS如何在生物…

新加坡博士申请|中国社科院-新加坡社科大学联合培养工商管理博士

新加坡博士申请|中国社科院-新加坡社科大学联合培养工商管理博士 【项目名称】中国社会科学院大学与新加坡新跃社科大学工商管理博士项目 【学制】最短3年&#xff0c;最长不超过7年 【学位证书】新加坡新跃社科大学工商管理博士学位 【招生对象】企业高管、咨询顾问及其他有…

智能舌诊应用开发:结合通义千问与OpenAI库

项目介绍 所有的项目都是基于 TailwindCSS 实现了响应式&#xff0c;同时支持网页端和移动端的显示效果。 这期尝试开发的 AI 应用是使用通义千问的大模型 API&#xff0c;开发一个 AI 看舌苔的应用。 整个项目的操作流程比较简单&#xff0c;第一屏用户上传自己的舌头的照片…

【JavaWeb程序设计】页面编程

目录 一、使用divCSS实现页面的布局 1. HTML结构代码 2. CSS样式代码 3. 运行截图 二、使用各类标签制作一个静态页面 1. 我做的页面运行截图 2. HTML结构代码 3. CSS代码 一、使用divCSS实现页面的布局 以下代码实现如图的页面布局&#xff0c;请完善相关代码 1. HT…

Docker的架构原理

例子可以想象成一个买手机的场景 clien可以想象 你个人 docker deamon &#xff1a;店员 images&#xff1a; 样机 regisitry&#xff1a; 手机仓库 container: 使用的手机 首先我要在店员买一个手机&#xff0c;店员发现是样机&#xff0c;但是仓库有&#xff0c;&…

SwiftUI九创建watchOS应用

代码下载 这篇教程让可以应用之前所学到的SwiftUI知识&#xff0c;把Landmarks应用从iOS平台迁移到watchOS平台上。在拷贝可以共用的数据和视图文件之前&#xff0c;需要先给项目中添加一个对应watchOS的Target编译目标。在所有 assets 就绪后&#xff0c;将自定义SwiftUI视图…

【TS】TypeScript 中的 any 与 unknown:理解与实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript 中的 any 与 unknown&#xff1a;理解与实践一、引言二、any&#x…

找不到msvcr110.dll是怎么回事?彻底解决msvcr110.dll丢失的方法

当您的电脑提示遇到msvcr110.dll丢失时&#xff0c;您知道如何解决此问题吗&#xff1f;事实上&#xff0c;解决此类dll文件丢失的问题相对较为简单。只要我们深入了解msvcr110.dll丢失的具体情况&#xff0c;便可轻松解决此问题。以下为您介绍msvcr110.dll修复方法。 一&#…

vue table表格 ( parseTime-格式化时间)

<el-table-column label"发布时间" width"420px" prop"bidPublishDatetime"><template slot-scope"scope"><span>{{ parseTime(scope.row.bidPublishDatetime, {y}-{m}-{d}) }}</span></template></…

从手工到智能:乐财业鹦鹉系统引领财税管理新纪元

随着税制改革的不断施行&#xff0c;我国数字经济顶层设计与地方推进举措相结合的政策体系已基本完善。中小企业的数字化转型尤为关键&#xff0c;也是大势所趋。 精益研发 数智创新 乐财业以敢于创新的精神&#xff0c;扎根在财税行业数字化的土壤&#xff0c;历时3年的精心研…

海豚调度监控:新增依赖缺失巡检,上游改动再也不用担心了!

&#x1f4a1; 本系列文章是 DolphinScheduler 由浅入深的教程&#xff0c;涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。 祝开卷有益:) 用过 DolphinScheduler 的小伙伴应该都知道&#xff0c;Dolphin…

SpringBoot整合DataX数据同步(自动生成job文件)

SpringBoot整合Datax数据同步 文章目录 SpringBoot整合Datax数据同步1.简介设计理念 DataX3.0框架设计DataX3.0核心架构核心模块介绍DataX调度流程 2.DataX3.0插件体系3.数据同步1.编写job的json文件2.进入bin目录下&#xff0c;执行文件 4.SpringBoot整合DataX生成Job文件并执…

【Linux】目录和文件的权限意义

现在我们知道了Linux系统内文件的三种身份&#xff08;拥有者、用户组与其他人&#xff09;&#xff0c;知道每种身份都有三种权限&#xff08;rwx&#xff09;&#xff0c;也知道能够使用chown、chgrp、chmod修改这些权限与属性&#xff0c;当然&#xff0c;利用IS-l去查看文件…

一文了解“大数据招商思维”,读懂什么是大数据招商!

近年来&#xff0c;随着大数据及人工智能等新一代信息技术的快速发展&#xff0c;数据作为重要的资源和资产&#xff0c;成为推动经济发展的核心驱动力&#xff0c;广泛应用于各个领域&#xff0c;深刻的改变着我们的生产和生活方式。那么对于“招商引资”来说&#xff0c;大数…

超级加密狗——CBS(赛博锁)

智能终端设备安全现状&#xff1a; 随着网络和智能终端普及&#xff0c;云管端的智能物联应用越来越多&#xff0c;如何保证云端平台安全&#xff0c;以及各种智能终端&#xff08;含智能仪器&#xff0c;车载终端、智能摄像头、工控机、网关路由器、智能设备、 IoT设备等&…

3D模型格式转换工具HOOPS Exchange如何实现对PRC文档的支持?

随着三维模型在各个行业中的应用越来越广泛&#xff0c;高效、准确的3D模型格式转换工具变得尤为重要。在众多工具中&#xff0c;HOOPS Exchange因其强大的功能和广泛的格式支持赢得了用户的青睐。本文将详细探讨HOOPS Exchange如何实现对PRC&#xff08;Product Representatio…