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 = true


console.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 = 100

obj()

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: string
    gender: string
    constructor(name: string, gender: string) {
        this.name = name
        this.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 : number
    y : number
    constructor(x:number,y:number){
        this.x = x
        this.y = y
    }
    /**
     * 计算方法
     * @param num  计算的比例数
     */
    scale(num: number):void{
        this.x *= num
        this.y *= num
        console.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: number
    constructor(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: number
    y: number
}

class NewPoint2d {
    x: number
    y: 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: number
    y: number
    constructor(x:number,y:number){
        this.x = x
        this.y = y
    }
}

class NewPoint3d {
    x: number
    y: number
    z: number
    constructor(x:number,y:number,z:number){
        this.x = x
        this.y = y
        this.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: number
    y: number

}

interface NewPoint3d {
    x: number
    y: number
    z: number
}

// 类 和 接口之间也是相互兼容的
class  NewPoint4d {
    x: number
    y: number
    z: number

    constructor(x:number,y:number,z:number){
        this.x = x
        this.y = y
        this.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) => void


type F2 = (a: number, b: number) => void


// 这里如果不给声明变量赋值初始值会报错
let f1: F1 = (a: number) => { }
let f2: F2 = (a: number, b: number) => { }
f2 = f1

console.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) => string


type F2 = (a: number, b: number) => string


let f1: F1 = (a: number) => ''

// 参数少的兼容参数多的,但是参数多的不能兼容参数少的
let f2: F2 = f1

console.log('f2的类型是:', f2);

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

image-20240616083608379

返回值

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

返回值是原始类型
// 下面这个是错误的一个演示

type F1 = (a: number) => number


type F2 = (a: number, b: number) => string

let f1: F1 = (a: number) => 0
let f2: F2 = f1

// 正确写法
type F1 = (a: number) => string


type F2 = (a: number, b: number) => string

let 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 = f2

console.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 & Concat

let 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: NumType
    add: (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 };

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

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

相关文章

昇思25天学习打卡营第4天|扩散模型

文章目录 昇思MindSpore应用实践基于MindSpore的Diffusion扩散模型1、Diffusion Models 简介2、构建 Diffusion Model 的准备工作3、Attention 机制4、条件 U-Net5、Diffusion 正向过程6、Diffusion 反向过程7、Diffusion 模型训练 Reference 昇思MindSpore应用实践 本系列文章…

掌握Python编程的深层技能

一、Python基础语法、变量、列表、字典等运用 1.运行python程序的两种方式 1.交互式即时得到程序的运行结果 2.脚本方式把程序写到文件里(约定俗称文件名后缀为.py),然后用python解释器解释执行其中的内容2.python程序运行的三个步骤 python3.8 C:\a\b\c.py 1.先启动python3…

什么是产线工控安全,如何保障产线设备的安全

什么是产线工控安全&#xff1f; 工控&#xff0c;指的是工业自动化控制&#xff0c;主要利用电子电气、机械、软件组合实现。即是工业控制系统&#xff0c;或者是工厂自动化控制。产线工控安全指的是工业控制系统的数据、网络和系统安全。随着工业信息化的迅猛发展&#xff0…

【Lua】第一篇:在Linux系统中安装搭建lua5.4.1环境

文章目录 一. 远程下载安装包二. 解压安装包三. 编译安装Lua环境 一. 远程下载安装包 输入以下命令即可在当前目录下&#xff0c;远程下载安装包lua-5.4.1.tar.gz&#xff1a; wget http://www.lua.org/ftp/lua-5.4.1.tar.gzPS&#xff1a;其他版本的安装包如下&#xff0c;可…

鸿蒙项目实战-月木学途:1.编写首页,包括搜索栏、轮播图、宫格

效果展示 搜索栏制作 相关知识回顾 输入框组件TextInput 单行输入框类型.type(InputType.Normal)//基本输入框.type(InputType.Password)//密码.type(InputType.Email)//邮箱.type(InputType.Number)//数字.type(InputType.PhoneNumber)//电话号.type(InputType.Normal).type…

【折腾手机】一加6T刷机postmarketOS经历和体验

写在前面 到目前为止&#xff0c;我已经花了非常多的时间去学习和了解x86架构和RISC-V架构&#xff0c;对它们的指令集编程、指令格式的设计、编译套件的使用都亲自去体会和实践过&#xff0c;学到了很多的东西。但是对于离我们最近的arm架构却了解甚少。为什么说离我们最近呢…

Python | Leetcode Python题解之第199题二叉树的右视图

题目&#xff1a; 题解&#xff1a; class Solution:def rightSideView(self, root: TreeNode) -> List[int]:rightmost_value_at_depth dict() # 深度为索引&#xff0c;存放节点的值max_depth -1stack [(root, 0)]while stack:node, depth stack.pop()if node is not…

15 个适用于企业的生成式 AI 用例

作者&#xff1a;来自 Elastic Jennifer Klinger 关于生成式人工智能及其能做什么&#xff08;和不能做什么&#xff09;有很多讨论。生成式人工智能&#xff08;例如大型语言模型 - LLMs&#xff09;利用从大量训练数据中学习到的模式和结构来创建原创内容&#xff0c;而无需存…

weiyang**2.部署

一、官方文档 一键部署可以在 同机 快速搭建WeBASE管理台环境&#xff0c;方便用户快速体验WeBASE管理平台。 一键部署会搭建&#xff1a;节点&#xff08;FISCO-BCOS 2.0&#xff09;、管理平台&#xff08;WeBASE-Web&#xff09;、节点管理子系统&#xff08;WeBASE-Node-…

3D生物打印的未来:多材料技术的突破

多材料生物打印技术是近年来发展迅速的一项技术&#xff0c;为组织工程和再生医学带来了新的机遇&#xff0c;可以帮助我们更好地理解人体组织的结构和功能&#xff0c;并开发新的治疗方法。 1. 组织构建 复杂性模拟&#xff1a;多材料生物打印技术能够构建具有层次结构和异质…

2022年第十三届蓝桥杯比赛Java B组 【全部真题答案解析-第二部分】

上一篇文章&#xff1a;2022年第十三届蓝桥杯比赛Java B组 【全部真题答案解析-第一部分】_尘封的CPU的博客-CSDN博客最近回顾了Java B组的试题&#xff0c;深有感触&#xff1a;脑子长时间不用会锈住&#xff0c;很可怕。兄弟们&#xff0c;都给我从被窝里爬起来&#xff0c;赶…

综合项目实战--jenkins节点模式

一、DevOps流程 DevOps是一种方法论,是一系列可以帮助开发者和运维人员在实现各自目标的前提下,向自己的客户或用户交付最大化价值及最高质量成果的基本原则和实践,能让开发、测试、运维效率协同工作的方法。 DevOps流程(自动化测试部分) DevOps完整流程 二、gitee+j…

Burpsuite靶场中信息泄露相关的实验通关

目录 第一关&#xff1a;错误消息中的信息披露 第二关&#xff1a;调试页面信息披露 第三关&#xff1a;通过备份文件披露源代码 第四关&#xff1a;通过信息披露绕过身份验证 第五关&#xff1a;版本控制历史中的信息披露 最近看大佬的文章&#xff0c;发现了很对自己没有…

IOS Swift 从入门到精通:ios 连接数据库 安装 Firebase 和 Firestore

创建 Firebase 项目 导航到Firebase 控制台并创建一个新项目。为项目指定任意名称。 在这里插入图片描述 下一步,启用 Google Analytics,因为我们稍后会用到它来发送推送通知。 在这里插入图片描述 在下一个屏幕上,选择您的 Google Analytics 帐户(如果已创建)。如果没…

FFT的IP核使用报错的检查流程

一、config部分 拉出clk resetn, s_axis_config_tdata&#xff0c; s_axis_config_tready, s_axis_config_tvalid .这四个信号。 时序行为解释&#xff1a;

【python - 数据】

一、序列 序列&#xff08;sequence&#xff09;是一组有顺序的值的集合&#xff0c;是计算机科学中的一个强大且基本的抽象概念。序列并不是特定内置类型或抽象数据表示的实例&#xff0c;而是一个包含不同类型数据间共享行为的集合。也就是说&#xff0c;序列有很多种类&…

第0章_项目方案介绍

文章目录 第0章 项目方案介绍0.1 功能介绍0.2 硬件方案0.3 软件方案0.3.1 上位机方案0.3.2 **中控方案**0.3.3 **传感器方案**0.3.4 **技术难点** 第0章 项目方案介绍 0.1 功能介绍 本课程来自一个真实项目&#xff1a;多个气体传感器的管理。由于气体传感器比较昂贵&#xf…

mysql5.7安装使用

mysql5.7安装包&#xff1a;百度网盘 提取码: 0000 一、 安装步骤 双击安装文件 选择我接受许可条款–Next 选择自定义安装&#xff0c;下一步 选择电脑对应的系统版本后(我的系统是64位)&#xff0c;点击中间的右箭头&#xff0c;选择Next 选择安装路径–Next 执行…

第1章:计算机系统知识

第1章&#xff1a;计算机系统知识 校验码 海明码 1、数据怎么分组&#xff0c;为什么这样分组&#xff1f; 分组1&#xff1a;1、3、5、7 分组2&#xff1a;2、3、6、7 分组3&#xff1a;4、5、6、7 目的就是为了纠错&#xff0c;从下面图便可以知道 2、为什么检验位在2∧…

动态应用安全测试 (DAST) 与渗透测试:应用程序安全测试综合指南

二十多年来,Web 应用程序一直是许多企业的支柱,因此其安全性至关重要。 动态应用程序安全测试 (DAST) 和渗透测试对于识别和缓解 Web 应用程序安全中的安全漏洞至关重要。 虽然两者都旨在增强应用程序安全性,但它们在方法、执行和结果方面存在很大差异。 本综合指南将探讨…