TypeScript快速入门

news2024/11/29 4:27:14

TypeScript快速入门

    • 1.TypeScript介绍
      • 1.1.TypeScript为什么要为JS添加类型支持
      • 1.2.TypeScript相比JS优势
    • 2.TypeScript初体验
      • 2.1.安装编译TS的工具包
      • 2.2.编译并运行TS代码
      • 2.3.简化运行TS代码
    • 3.TypeScript常用类型
      • 3.1.类型注解
      • 3.2.常用基础类型
      • 3.3.原始类型 number/string/boolean/null/undefined/symbol
      • 3.4.对象类型(数组)
      • 3.5.类型别名(自定义类型)
      • 3.6.函数类型
        • void类型
        • 可选参数
      • 3.7.对象类型
        • 可选属性
      • 3.8.接口
        • 接口和类型别名对比
        • 接口继承
      • 3.9.元组
      • 3.10.类型推论
      • 3.11.类型断言
      • 3.12.字面量类型
      • 3.13.枚举类型
      • 3.14.any类型
      • 3.15.typeof
    • 4.TypeScript高级类型
      • 4.1. class类
        • class基本使用:
        • 构造函数
        • 继承extends,implement
        • 可见性修饰符public,protected,private
        • 只读修饰符readonly
      • 4.2.类型兼容性
        • 类之间兼容性
        • 接口之间兼容性
        • 函数直接兼容性
      • 4.3.交叉类型(&)
      • 4.4.泛型 和 keyof
        • 泛型
        • 泛型约束
        • 泛型接口
        • 泛型类
        • 泛型工具类型Partial,Readonly,Pick,Record
      • 4.5.索引签名类型 和 索引查询类型
        • 索引签名类型
        • 映射类型
        • 索引查询类型
    • 5.TypeScript类型声明文件
      • TS 的两种文件类型
      • 类型声明文件的使用说明
        • 使用已有的类型声明文件
        • 创建自己的类型声明文件

1.TypeScript介绍

typescript是js的超集,主要学习ts里面的原始类型、字面量类型、数组类型、函数类型、类类型、接口类型、类型别名、联合与交叉类型、枚举类型、泛型等类型元素,以及类型推断、类型断言、类型缩小、类型放大等特性。相较于js更加严谨,编写代码的时候静态类型的校验。
在这里插入图片描述

1.1.TypeScript为什么要为JS添加类型支持

背景:JS的类型系统有“先天缺陷”,JS代码中的大多数错误都是类型错误Uncaught TypeError。
问题:增加了发现和纠正bug的时间,严重影响了开发效率。

在编程语言的动态性方面,TypeScript是静态类型的编程语言,JS是动态类型的编程语言。
静态类型:在编译时进行类型检查;
动态类型:在执行过程中进行类型检查。
代码编译和代码执行顺序:1编译2执行。

对于JS:你需要等到代码实际执行时才发现错误(后期)。
对于TS:在编译代码时(在执行之前)发现错误
并且,通过VSCode等开发工具,TS可以在编写代码时提前发现代码中的错误,减少了找Bug和改Bug的时间。

1.2.TypeScript相比JS优势

  1. 尽早发现错误(在编写代码时),减少找Bug和改Bug的时间。,提高开发效率。
  2. 程序任何位置的代码都有代码提示,可以随时随地提供一种安全感,增强开发体验。
  3. 强大类型系统提高了代码的可维护性,使重构代码变得更容易。
  4. 支持最新的ECMAScript语法,优先体验最新的语法,让您走在前端技术的最前沿。
  5. TS类型推断机制不需要在代码中处处显示注释类型,它允许您在最小化成本的同时享受其优势。

除此之外,随着vue3源码用TS重写,Angular默认支持TS, React和TS完美配合,TypeScript已经成为大中型前端项目的首选编程语言。

2.TypeScript初体验

2.1.安装编译TS的工具包

问题:为什么要安装编译TS的工具包?
回答:Node.js/ 浏览器,只认识JS代码,不认识TS代码。在运行TS代码之前,需要将其转换为JS代码。
在这里插入图片描述

全局安装命令:

npm i -g typescript@4.5.2

typescript包:编译TS代码的包,提供tsc命令转换TS->JS。
验证是否安装成功:tsc-v(查看typescript版本)

tsc -v

2.2.编译并运行TS代码

  1. 创建hello.ts文件(注意TS文件的后缀名为.ts)
  2. 将TS 编译 JS,在终端输入tsc hello.ts命令。(此时,一个同名的JS文件将出现在同级目录中)
tsc hello.ts
  1. 执行JS代码:在终端中输入命令node hello.js
node hello.js

在这里插入图片描述
说明:所有合法的JS代码都是TS代码,有JS基础只需要学习TS类型即可
注意:由TS编译生成的JS文件,代码中就没有类型信息了

2.3.简化运行TS代码

使用ts-node包,直接在node.js中执行代码
安装命令:

npm i -g ts-node 

使用方式:

ts-node hello.ts

3.TypeScript常用类型

3.1.类型注解

let age: number = 24

在这里插入图片描述
说明:代码中 : number就是类型注解
作用:为变量添加约束条件,约定age的类型为number,如果赋值其他类型,就会报错
在这里插入图片描述

3.2.常用基础类型

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

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

3.3.原始类型 number/string/boolean/null/undefined/symbol

let num: number = 1; // number
let str: string = "2"; // string
let bool: boolean = true; // boolean
let nul:null = null; // null
let undef: undefined = undefined; // undefined
let sy: symbol = Symbol(); // symbol

3.4.对象类型(数组)

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

数组类型的两种写法

let strArr: string[] = ['a', 'b'] // 推荐使用这种
let numArr: Array<number> = [1, 2, 3]

数组中既有number类型,又有string类型

let numOrStrArr: (number | string)[] = [1, 'a']

解释:|(竖线)在TS中叫做联合类型(由两个以上其他类型组成的类型,可以表示这些类型中任意一种)

let age: number | string = 20; // 表示既可以是number,又可以是string

let arr: (number | string)[] = [1, 3, 5, 'a', 'b'] // 添加小括号表示:首先是数组,数组中可以出现number或string

let arr1: number | string[] = ['a','b']let arr1: number | string[] = 123 // 不添加小括号表示:可以是number,或者string数组

3.5.类型别名(自定义类型)

类型别名:为任意类型起别名
使用场景:当同一类型被多次使用时,可以通过类型别名,简化该类型的使用
格式:type 别名名称 = 类型定义

type CustomArray = (number | string)[]

let arr: CustomArray = [1, 'a']

3.6.函数类型

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

方式一:单独指定参数,返回值的类型

function add(num1: number, num2: number): number {
  return num1 + num2
}

const add1 = (num1: number, num2: number): number => {
  return num1 + num2
}

方式二:同时指定参数,返回值类型

const add2: (num1: number, num2: number) => number = (num1, num2 ) => {
  return num1 + num2
}

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

void类型

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

function greet(name: string): void {
  console.log('Hello', name)
}

可选参数

使用函数实现某些功能时,参数可传可不传,这时候就用到可选参数了
语法:在可传可不传的参数名称后面添加?(问号)

function mySlice(start?: number, end?: number): void {
  console.log('开始:', start, '结束', end)
}

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能出现必选参数

3.7.对象类型

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

let gf : { name: string; age: number; sayLove(): void} = {
  name: 'yj',
  age: 26,
  sayLove() {
    console.log("honey")
  },
} 

let gf1 : { 
  name: string
  age: number
  sayLove(): void
} = {
  name: 'yj',
  age: 26,
  sayLove() {
    console.log("honey")
  },
}
  1. 直接使用{}来描述对象结构。采用=属性名:类型=的形式;方法采用方法名():返回值类型的形式
  2. 如果方法有参数,就在方法名后面的小括号中中指定参数类型(比如:greet(name:string):void
  3. 在一行代码中指定对象的多个属性类型时,使用;(分号)来分隔。
  • 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉;(分号)
  • 方法的类型也可以使用箭头函数的形式(比如:{sayHi:()=>void}

可选属性

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

function myAxios(config: { url: string; method?: string }){  
  console.log(config)
}

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

3.8.接口

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

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

let person: IPerson = {
  name: 'dsc',
  age: 24,
  sayHi() {}
}

接口和类型别名对比

实际上,在大多数的情况下使用接口类型和类型别名的效果等价,但是在某些特定的场景下这两者还是存在很大区别。

  1. 重复定义的接口类型,它的属性会叠加,这个特性使得我们可以极其方便地对全局变量、第三方库的类型做扩展
  2. 如果我们重复定义类型别名,那么就会报错
    在这里插入图片描述

接口继承

如果两个接口之间有相同的属性或方法, 可以将公共的属性或方法抽离出来, 通过继承来实现复用
比如,这两个接口都有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的所有属性和方法(此时, Point3D同时有x、y、z三个属性)。

3.9.元组

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

let position: number[] = [39.5427 116.23171]

使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。
更好的方式:元组(Tuple) 。
元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型

let position: [ numbernumber] = [39 .5427116.2317] 

解释:

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

3.10.类型推论

在TS中,某些没有明确指出类型的地方,TS的类型推论机制会帮助提供类型
换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写!
发生类型推论的2种常见场景:

  1. 声明变量并初始化时
  2. 决定函数返回值时。

在这里插入图片描述

注意:这两种情况下,类型注解可以省略不写!
推荐:能省略类型注解的地方就省略(偷懒,充分利用TS类型推论的能力,提升开发效率)。
技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型。

3.11.类型断言

有时候开发人员会比TS更加明确一个值的类型, 此时, 可以使用类型断言来指定更具体的类型。比如,

<a href="http://www.baidu.com/" id="link">百度</a>

在这里插入图片描述

注意: getElementById方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a标签 特有的 href 等属性。
因此,这个类型太宽泛(不具体), 无法操作href等 a标签特有的属性或方法。
解决方式:这种情况下就需要使用类型断言指定更加具体的类型

使用类型断言:

解释:

  1. 使用as关键字实现类型断言
  2. 关键字as 后面的类型是一个更加具体的类型 (HTMLAnchorElement是 HTMLElement 的子类型)
  3. 通过类型断言,aLink的类型变得更加具体, 这样就可以访问a标签特有的属性或方法了
    另一种语法,使用<>语法, 这种语法形式不常用知道即可:
    在这里插入图片描述

技巧:在浏览器控制台,通过console.dir() 打印 DOM 元素,在属性列表的最后面, 即可看到该元素的类型。

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字面量(比如, 对象、数字等)都可以作为类型使用。

使用模式:字面量类型配合联合类型一起使用
使用场景: `用来表示一组明确的可选值列表``
比如,游戏的方向可选值上下左右中任意一个

function changDirection(direction: 'up' | 'down' | 'left' | 'right') {
  console.log(direction)
}

相比于string类型,使用字面量类型更加精确,严谨

3.13.枚举类型

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

enum Direction { Up, Down, Left, Right }

enum Direction {
	Up = 'UP',
	Down = 'DOWN',
	Left = 'LEFT',
	Right = 'RIGHT'
}

枚举的作用在于定义被命名的常量集合,一个默认从 0 开始递增的数字集合,称之为数字枚举。也可以指定值,这里可以指定的值可以是数字或者字符串。

enum Days {
    Sunday = 1,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}
let day = Days.Sunday;

字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值
在这里插入图片描述

3.14.any类型

原则:不推荐使用any! 这会让TypeScript变为"AnyScript"(失去TS类型保护优势)
因为当值的类型为any时,可以对该值进行任何操作,并且不会有代码提示
在这里插入图片描述
以上代码都不会有任何类型的错误提示,即使存在错误

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

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

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

3.15.typeof

typeof 的主要用途是在类型上下文中获取变量或者属性的类型(类型查询)

// 推断变量的类型
let strA = "2";
type KeyOfType = typeof strA; // string

// 反推出对象的类型作为新的类型
let person = {
    name: '张三',
    getName(name: string):void {
        console.log(name);
    }
}
type Person = typeof person;

// 根据已有变量的值,获取该值类型,简化类型书写
let p = { x: 1, y: 2 }
function fornatPoint(point: typeof p) {}

4.TypeScript高级类型

4.1. class类

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

class基本使用:

在这里插入图片描述

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

实例属性初始化:
在这里插入图片描述

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

实例方法:
在这里插入图片描述
方法的类型注解(参数和返回值)与函数用法相同

构造函数

class Person {
	age: number
	gender: string

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

在这里插入图片描述

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

继承extends,implement

class Animal {
  move() {
    console.log('move')
  }
}

class Dog extends Animal {
  bark() {
    console.log("汪汪汪")
  }
}

const dog = new Dog()
  1. 通过extends关键字实现继承
  2. 子类Dog继承父类Animal,则Dog的实例对象dog就同时具有了父类Animal和子类Dog的所有属性和方法
interface Singable {
  sing(): void
}

class Person implements Singable {
  sing() {
    console.log("你我山前没相见山后别相逢")
  }
}
  1. 通过implements关键字让class实现接口
  2. Person类中必须提供Singable接口中指定的所有方法和属性

可见性修饰符public,protected,private

类成员可见性:可以用TS来控制class的方法或属性对于class外的代码是否可见
可见性修饰符包括:public(公有的),protected(受保护的),private(私有的)
public:公有成员可以被任何地方访问,默认可见性,可以直接省略
在这里插入图片描述
protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见,实例不可见
在这里插入图片描述
private:表示私有的,只在当前类可见,对实例对象以及子类也是不可见
在这里插入图片描述

只读修饰符readonly

readonly:表示只读,用来防止在构造函数之外对属性进行赋值
在这里插入图片描述

  1. readonly只能修饰属性,不能修饰方法
  2. 属性后面类型注解如果不加,则类型为字面量类型
  3. 接口或者{}表示的对象类型,也可以使用readonly

4.2.类型兼容性

两种类型系统:1.StructuralType System(结构化类型系统)2 .NominalType System(标明类型系统)
TS 采用的是结构化类型系统,也叫做 ducktyping(鸭子类型),类型检查关注的是值所具有的形状
也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。

类之间兼容性

在这里插入图片描述

  1. Point 和 Point2D 是两个名称不同的类。

  2. 变量 p 的类型被显示标注为 Point 类型,但是它的值却是 Point2D 的实例,并且没有类型错误。

  3. 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。

  4. 但是,如果在 Nominal Type System 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。

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

接口之间兼容性

接口之间兼容性类似于class,并且class和interface之间也可以兼容
在这里插入图片描述
在这里插入图片描述

函数直接兼容性

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

  1. 参数个数,参数多的兼容参数少的
    在这里插入图片描述
    在这里插入图片描述

  2. 参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)
    在这里插入图片描述

  3. 返回值类型,只关注返回值类型本身即可
    在这里插入图片描述
    如果返回值类型是原始类型,此时两个类型要相同
    在这里插入图片描述
    如果返回值类型是对象类型,此时成员多的可以赋值给成员少的

4.3.交叉类型(&)

功能类似于接口继承,用于组合多个类型为一个类型(常用于对象类型)

interface Person { name: string }
interface Contact { phone: string }
type PersonDetail = Person & Contact

let obj: PersonDetail = {
  name: 'dsc',
  phone: '176...'
}

交叉类型和接口继承的对比

  • 相同点:都可以实现对象类型的组合
  • 不同点:两种方式实现组合时,对于同名属性间,处理类型冲突的方式不同
    在这里插入图片描述
    以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单理解为:
    方法重载
fn: (value: string | number) => string

4.4.泛型 和 keyof

泛型

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class 中。需求:创建一个 id 函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)。

function id (value: number): number { return value }

比如,id (10) 调用以上函数就会直接返回 10 本身。但是,该函数只接收数值类型,无法用于其他类型。

为了能让函数能够接受任意类型,可以将参数类型修改为 any。但是,这样就失去了 TS 的类型保护,类型不安全。

function id (value: any): any { return value }

泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用。实际上,在 C#和 Java 等编程语言中,泛型都是用来实现可复用组件功能的主要工具之一。

  • 创建泛型函数
function id<Type> (value: Type): Type {return value}
  1. 语法:在函数名称的后面添加<>(尖括号),尖括号中添加类型变量,比如此处的 Type。

  2. 类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。

  3. 该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。

  4. 因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。

  5. 类型变量 Type,可以是任意合法的变量名称。

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

同样,如果传入类型 string,函数 id 参数和返回值的类型就都是 string。

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

  • 简化泛型函数调用
let num = id(10)
  1. 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用

  2. 此时,TS 内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量 Type 的类型。

  3. 比如,传入实参 10,TS 会自动推断出变量 num 的类型 number,并作为 Type 的类型。

推荐:使用这种简化的方式调用泛型函数,使代码更短,更易于阅读。

说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数。

泛型约束

泛型约束:默认情况下,泛型函数的类型变量 Type 可以代表多个类型,这导致无法访问任何属性。

比如,id (‘a’)调用函数时获取参数的长度:

function id<Type>(value: Type): Type{
	console.log (value. length)
	return value
}

解释:Type 可以代表任意类型,无法保证一定存在 length 属性,比如 number 类型就没有 length。

此时,就需要为泛型添加约束来收缩类型(缩窄类型取值范围)。

方式一:指定更加具体的类型

function id<Type>(value: Type[]): Type[] {
	console.log(value.length)
	return value
}

方式二:添加约束

interface ILength { length: number }
function id<Type extends ILength>(value: Type): Type {
	console.log(value.length)
	return value
}

该约束表示:传入的类型必须具有length属性

泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)。比如,创建一个函数来获取对象中属性的值:


function getProp<Type, Key extends keyof Type> (obj: Type, key: Key){
	return obj [key]
}
let person = { name: 'jack', age: 18 } 
getProp (person, 'name')

解释:

  1. 添加了第二个类型变量 Key,两个类型变量之间使用(,)逗号分隔。

  2. Keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。

  3. 本示例中 keyof Type 实际上获取的是 person 对象所有键的联合类型,也就是:‘name’|‘age’。

  4. 类型变量 Key 受 Type 约束,可以理解为:Key 只能是 Type 所有键中的任意一个,或者说只能访问对象中存在的属性。

泛型接口

接口也可以配合泛型来使用,以增加其灵活性,增加其复用性
在这里插入图片描述

  1. 在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口。

  2. 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量

  3. 使用泛型接口时,需要显式指定具体的类型(比如,此处的 IdFunc < number>)。

  4. 此时,id 方法的参数和返回值类型都是 number; ids 方法的返回值类型是 number[ ]。

泛型类

class也可以配合泛型来使用
比如,React的class组件的基类Component就是泛型类,不同的组件有不同的props和state
在这里插入图片描述
创建泛型类

class GenericNumber<NumType> {
	defaultValue: NumType
	add: (x:NumType, y:NumType) => NumType
}

使用泛型类

const myNum = new GenericNumber<number>()
myNum.defaultValue = 10

泛型工具类型Partial,Readonly,Pick,Record

Partial< Type> 用来构造(创建)一个类型,讲Type的所有属性设置为可选

interface Props {
	id: string
	children: number[]
}
type PartialProps = Partial<Props>

构造出来的新类型PartialProps结构和Props相同,但所有属性都变为可选的

Readonly< Type> 用来构造(创建)一个类型,讲Type的所有属性设置为只读

interface Props {
	id: string
	children: number[]
}
type PartialProps = Partial<Props>
let props: ReadonlyProps = { id:'1', children: []}
props.id = '2' // 报错,无法分配,因为它是只读

构造出来的新类型PartialProps结构和Props相同,但所有属性都变为只读的

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

interface Props {
	id: string 
	title: string
	children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
  1. Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。
  2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
  3. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
  4. 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。

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

type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj:RecordObj = {
	a: ['1'],
	b: ['2'],
	c: ['3']
}
  1. Record工具类型有两个类型变量:1表示对象有哪些属性2表示对象属性的类型
  2. 构建的新对象类型RecordObj表示:这个对象有三个属性分别为a/b/c,属性值的类型都是string[]

4.5.索引签名类型 和 索引查询类型

索引签名类型

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

使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了。

在这里插入图片描述

  1. 使用[key: string]来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称,都可以出现在对象中。

  2. 这样,对象 obj 中就可以出现任意多个属性(比如,a、b等)。

  3. key 只是一个占位符,可以换成任意合法的变量名称。

  4. 隐藏的前置知识:JS 中对象({})的键是 string 类型的
    在这里插入图片描述
    在JS中数组是一类特殊的对象,特殊在数组的键(索引)是数值类型

映射类型

映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。

比如,类型PropKeys有x/y/z,另一个类型Type1中也有x/y/z,并且Type1中x/y/z的类型相同:

type PropKeys = 'x' | 'y' | 'z'

type Type1 = { x: number; y: number; z: number }

这样书写没错,但x/y/z重复书写了两次。像这种情况,就可以使用映射类型来进行简化。

type PropKeys = 'x' | 'y' | 'z'

type Type2 ={ [Key in PropKeys]: number }
  1. 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了[ ]

  2. Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin (let k in obj)。

  3. 使用映射类型创建的新对象类型 Type2 和类型 Type1 结构完全相同。

  4. 注意:映射类型只能在类型别名中使用,不能在接口中使用

映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建

type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }
  1. 首先执行`keyof Props获取对象类型Props中所有键的联合类型,即’a’|‘b’|‘c’
  2. 然后key in ...就表示key可以是Props中所有键的任意一个

泛型工具类Partial实现
在这里插入图片描述

索引查询类型

刚刚用到的T[P]语法,在TS中叫做索引查询(访问)类型
作用:用来查询属性的类型

type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a']
  1. Props['a']表示查询类型Props中属性a对应的类型number,所以TypeA的类型为number
  2. [ ]中的属性必须存在于被查询类型中,否则报错

同时查询多个索引的类型

type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a''b'] // 结果为: string | number

type TypeB = Props[keyof Props] // 结果为: string | number | boolean

5.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 文件,仅用于提供类型信息。

    1. 用途:为js 提供类型信息。

总结:.ts 是 implementation(代码实现文件).d.ts 是 declaration(类型声明文件) 。如果要为 JS 库提供类型信息,要使用.d.ts 文件。

类型声明文件的使用说明

使用已有的类型声明文件

使用已有的类型声明文件:1 内置类型声明文件 2 第三方库的类型声明文件。
内置类型声明文件TS 为 JS 运行时可用的所有标准化内置 API 都提供了声明文件。比如,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:
在这里插入图片描述

实际上这都是 TS 提供的内置类型声明文件。

可以通过 Ctrl+鼠标左键(Mac: option+鼠标左键)来查看内置类型声明文件内容。

比如,查看 forEach 方法的类型声明,在 VSCode中会自动跳转到lib.es5…d.ts 类型声明文件中。当然,像 window、document 等 BOM、DOMAPI也都有相应的类型声明(lib.dom.d.ts)。

第三方库的类型声明文件:目前,几乎所有常用的第三方库都有相应的类型声明文件。第三方库的类型声明文件有两种存在形式:1 库自带类型声明文件 2 由 DefinitelyTyped 提供。

  1. 库自带类型声明文件:比如,axios。

在这里插入图片描述
这种情况下,正常导入该库,TS 就会自动加载库自己的类型声明文件,以提供该库的类型声明。

  1. 由DefinitelyTyped提供。

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

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

说明:在实际项目开发时,如果你使用的第三方库没有自带的声明文件,VSCode 会给出明确的提示。

在这里插入图片描述

解释:当安装@types/*类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明。

创建自己的类型声明文件

创建自己的类型声明文件:1 项目内共享类型 2 为已有 JS 文件提供类型声明。

项目内共享类型

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

操作步骤:

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

  2. 创建需要共享的类型,并使用export导出(TS中的类型也可以使用import/export实现模块化功能)。

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

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

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

注意:类型声明文件的编写与模块化方式相关,不同的模块化方式有不同的写法。但由于历史原因,JS 模块化的发展经历过多种变化(AMD、CommonJS、UMD、ESModule 等),而 TS 支持各种模块化形式的类型声明。这就导致,类型声明文件相关内容又多又杂。

说明:在导入.js 文件时,TS 会自动加载与.js 同名的.d.ts 文件,以提供类型声明。

declare 关键字:用于类型声明,为其他地方(比如,。Js 文件)已存在的变量声明类型,而不是创建一个新的变量

  1. 对于 type、interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。

  2. 对于 let、function 等具有双重含义(在 JS、TS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。

如有不足,请多指教,
未完待续,持续更新!
大家一起进步!

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

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

相关文章

前端基础html css js

html&#xff1a;框架 css&#xff1a;美化 jp&#xff1a;交互 HTML 1.基础标签 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>这是我的滴滴滴一个网页</title></head><body><h1>一号 标题&…

Pygame显示文字

使用Pygame显示文字的步骤如图1所示。图1 显示文字的步骤1 Pygame的初始化通过以下代码实现Pygame的初始化。import pygame pygame.init()其中&#xff0c;第1行代码的作用是在程序中导入pygame模块&#xff1b;第2行代码的作用是实现pygame的初始化。2 屏幕的创建使用如下代码…

CacheLib 原理说明

CacheLib 介绍 CacheLib 是 facebook 开源的一个用于访问和管理缓存数据的 C 库。它是一个线程安全的 API&#xff0c;使开发人员能够构建和自定义可扩展的并发缓存。 主要功能&#xff1a; 实现了针对 DRAM 和 NVM 的混合缓存&#xff0c;可以将从 DRAM 驱逐的缓存数据持久…

ESP8266 + STC15基于AT指令通过TCP通讯协议控制IO状态

ESP8266 + STC15基于AT指令通过TCP通讯协议控制IO状态 📌ESP8266 AT固件基于安信可AT固件,相关刷AT固件可以参考《NodeMCU-刷写AT固件》当然WIFI模块也可以是esp01. STC15 单片机采用的是:STC15F2K60S2 晶振频率采用内部:22.1184MHz🌼功能介绍 通过电脑端的网络调试助手…

【C++】类和对象(第一篇)

文章目录1. 面向过程和面向对象初步认识2.类的引入3.类的定义3.1 类的两种定义方式3.2 成员变量命名规则建议4. 类的访问限定符及封装4.1 访问限定符4.2 封装5. 类的作用域6. 类的实例化7. 类对象模型7.1 类对象大小的计算7.2 类对象的存储方式猜测7.3 结构体内存对齐规则复习8…

JDK15 新特性详解,2020-09-15 正式发布

预览版&#xff1a;该功能在当前版本可以使用&#xff0c;如果效果不是很好的话&#xff0c;可能以后的其他版本就会删去该功能。 最终版&#xff1a;该功能在之前版本效果很好&#xff0c;之后的每个版本中都会存在该功能。 Java 5 中的泛型&#xff0c;Java 8 中的 Lambda …

Linux云服务器下怎么重置MySQL8.0数据库密码

文章目录一、修改my.cnf配置文件为mysql免登陆二、免密登陆mysql三.给root用户重置密码1、首先查看当前root用户相关信息&#xff0c;在mysql数据库的user表中2、把root密码置为空3、退出mysql&#xff0c;删除/etc/my.cnf文件中添加进去的skip-grant-tables 重启mysql服务4、使…

FPGA实现AD9708和AD9280波形收发输出HDMI模拟示波器,串口协议帧控制显示,提供工程源码和技术支持

目录1、AD9708芯片解读和电路设计2、AD9280芯片解读和电路设计3、FPGA设计框架4、AD9708波形生成并发送5、AD9280采集接收波形6、HDMI波形显示算法7、串口协议帧控制波形显示8、vivado工程9、上板调试验证10、福利&#xff1a;工程源码获取1、AD9708芯片解读和电路设计 AD9708…

85024A是德科技keysight高频探头

附加功能&#xff1a; 易于执行在线测量出色的频率响应和单位增益探头灵敏度高低失真度规格输入电容&#xff08;在 500 MHz 时&#xff09;&#xff1a;< 0.7pF&#xff08;标称值&#xff09;输入电阻&#xff1a;1 MΩ&#xff08;标称值&#xff09;带宽&#xff1a;30…

2月8日刷题总结

写题一点思路也没有&#xff0c;题解也不能看得很懂。所以系统性的学习DP ing……跟着进度来&#xff0c;因此刷了一些已经刷过的类型的题&#xff08;也算再次熟悉一下&#xff09;P1077 [NOIP2012 普及组] 摆花题目描述小明的花店新开张&#xff0c;为了吸引顾客&#xff0c;…

力扣SQL刷题5

目录597. 好友申请 I&#xff1a;总体通过率602. 好友申请 II &#xff1a;谁有最多的好友603. 连续空余座位1045. 买下所有产品的客户597. 好友申请 I&#xff1a;总体通过率 官方讲的题目太繁琐了&#xff0c;大概就是&#xff08;表2中列1列2不全相同的行数&#xff09;/&a…

测试开发之Vue学习笔记-Vue路由

Vue路由18. Vue-路由基础安装 cnpm install --save vue-router官方文档&#xff1a;https://router.vuejs.org/zh/src/main.js中&#xff08;1&#xff09;引入VueRouter&#xff1a;import VueRouter from "vue-router"&#xff08;2&#xff09;使用VueRouter&…

《知行合一王阳明》读书笔记

《知行合一王阳明》用通俗易懂的语言介绍了王阳明一生的传奇经历和他的心学的核心思想。这篇读后感主要介绍一下我对心学的理解。在我看来&#xff0c;心学最本质的要求是“致良知”&#xff0c;最核心的方法论是“知行合一”。致良知是说要遵从自己的本心。王阳明相信人性本善…

(蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第1天(基础算法-上 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有75天

&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6; 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&a…

兔年新佳绩,思迈特软件获奖喜讯纷至沓来

近年来&#xff0c;随着大数据、云计算、人工智能、5G等技术加速创新&#xff0c;越来越多的ToB企业开始下沉探索边际&#xff0c;纵深业务服务场景&#xff0c;通过技术与行业的深度融合&#xff0c;为客户提供全面的转型服务&#xff0c;尽一切可能创造客户价值和商业价值。思…

【C++: list的模拟实现】

目录 1 list的简单回顾 2 类中成员变量的声明 3 __list_iterator 中运算符重载 4 list中的迭代器 5 list中增删查改以及clear 6 const迭代器 6.1 __list_iterator的重新实现 6.2 list类的巧妙修改 7 构造函数&&拷贝构造&&赋值运算符重载 8 反向迭代器…

金三银四丨黑蛋老师带你剖析-二进制漏洞

作者&#xff1a;黑蛋二进制漏洞岗上篇文章我们初步了解了一下简历投递方式以及二进制方向相关逆向岗位的要求&#xff0c;今天我们就来看看二进制漏洞相关的岗位&#xff0c;当然&#xff0c;漏洞岗位除了分不同平台&#xff0c;也有漏洞挖掘岗和漏洞分析利用岗。同样&#xf…

[人工智能-综述-11]:ChatGPT, 通用人工智能还是要来了

该来的还是要来的&#xff01;补充信息&#xff1a;ChatGPT是由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型&#xff0c;一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的语言来进行对话&#xff0c;还能根据聊天的上下文进行互动&…

C语言共用体(C语言union用法)详解

我们知道结构体&#xff08;Struct&#xff09;是一种构造类型或复杂类型&#xff0c;它可以包含多个类型不同的成员。在C语言中&#xff0c;还有另外一种和结构体非常类似的语法&#xff0c;叫做共用体&#xff08;Union&#xff09;&#xff0c;它的定义格式为&#xff1a;un…

STM32的HAL库分析及使用

STM32的HAL库分析及使用 STM32的三种开发方式 通常新手在入门STM32的时候&#xff0c;首先都要先选择一种要用的开发方式&#xff0c;不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库&#xff0c;而极少部分人会通过直接配置寄存器进行开发…