TypeScript学习 --- 其它内容补充

358 阅读6分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

字面量赋值

interface IUserInfo {
  name: string,
  age: number
}

function printUserInfo(info: IUserInfo) {
  console.log(info.name)
}

// error ===> 对传入的对象进行类型推导的时候,推导出的类型和接口类型不一致
printUserInfo({
  name: 'Klaus',
  age: 23,
  address: 'shanghai'
})
interface IUserInfo {
  name: string,
  age: number
}

function printUserInfo(info: IUserInfo) {
  console.log(info.name) // success
  console.log(info.address) // error
}

const userInfo = {
  name: 'Klaus',
  age: 23,
  address: 'shanghai'
}

// 此时实际上是引用传递
// 此时ts会进行类型擦除操作,即将多余的属性和方法去除以后
// 如果去除后的类型和所需要的类型一致,那么ts认为传入的类型是没有错误的
// 但是因为进行了类型擦除,所以此时传入的对象是不可以调用address属性的
printUserInfo(userInfo)

枚举

枚举其实就是将一组可能出现的值,一个个列举(穷举)出来,定义在一个类型中,这个类型就是枚举类型

枚举最大的好处就是给一些变量赋予可读性较强的名称,提升代码的可读性

枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型

// 枚举的各项为常量,所以一般都会全部大写
// 默认情况下,枚举是数字枚举,值从0开始依次递增
// 可以手动设置值,
// 1. 设置的值是数字,有设置值的枚举值前,依旧是从0开始依次递增,从设置了枚举值的哪一项起,按照设置的枚举值依次递增
// 2. 如果设置的值是字符串, 无法依次递增,每一个枚举项所对应的值都必须被设置
enum Driection {
  UP,
  DOWN,
  LEFT,
  RIGHT
}

泛型

泛型本质上就是类型参数化,即泛型是一个类型变量(type variable),只不过这个变量的值是类型

// <T> --- 定义类型变量 -- T只是一个变量的名称,可以任意取,但约定俗成一般为大写字母
// param: T --- 使用类型变量
function Foo<T>(param: T) {
  console.log(param)
}

// <number> --- 类型变量的赋值
Foo<number>(20)

// 前边类型变量的赋值可以省略,因为TS会自动进行类型推导
Foo('Klaus')

// 传入的参数类型可以不止一个
function Baz<K, V>(param1: K, param2: V) {
  console.log(param1, param2)
}
变量名说明
TType的缩写,类型
K、Vkey和value的缩写,键值对
EElement的缩写,元素
OObject的缩写,对象

泛型接口

// 泛型接口
interface IUser<T, V> {
  name: T,
  age: V
}

const user1: IUser<string, number> = {
  name: 'Klaus',
  age: 23
}

// 在使用泛型接口的时候,我们必须手动指定泛型的值
// 因为此时ts无法准确推导出具体泛型的值
const user2: IUser = { // error
  name: 'Klaus',
  age: 23
}

泛型类

// 泛型类
class Point<T> {
  x: T
  y: T

  constructor(x: T, y: T) {
    this.x = x
    this.y = y
  }
}

// 此时类的泛型值是可以使用类型推导推导出来的
// 因为new的本质就是在调用类的构造器方法 也就是初始化类的实例本质上就是在调用方法
const p1 = new Point(3, 4)

// 手动指定泛型值方式1
const p2 = new Point<number>(3,4)

// 手动指定泛型值方式2
const p3: Point<number> = new Point(3,4)
// 类似于 const arr: Array<number> = [2, 3, 4]

泛型约束

泛型约束就是对传入的类型的值进行一个约束

interface ILength {
  length: number
}

// T继承自ILength --- 所以传入的类型必须是有属性length的类型
function getLength<T extends ILength>(v: T) {
  return v.length
}

命名空间

TypeScript支持两种方式来控制我们的作用域

  1. 命名空间:通过namespace来声明一个命名空间 (ts在ESM提出之前实现的模块化方式,现在不常用)
  2. 模块化:每个文件可以是一个独立的模块,支持ES Module,也支持CommonJS

命名空间在TypeScript早期时,称之为内部模块,主要目的是将一个模块内部再进行作用域的划分,防止一些命名 冲突的问题

// 每一个命令空间都是一个模块内部的独立小模块
namespace foo {
  let name = 'foo'

  // 需要外部使用的时候,需要导出
  export function printName() {
    console.log(name)
  }
}

// 如果需要模块外的文件也可以使用命名空间中暴露的属性和方法的时候,需要导出命名空间
export namespace baz {
  let name = 'baz'

  export function printName() {
    console.log(name)
  }
}

// 调用命名空间中对应的方法
foo.printName()
baz.printName()

类型查找

类型声明文件

ts对于js最大的作用就是添加了类型约束,但是有的时候,我们希望在ts中引入js文件,

js文件本身并没有类型约束,直接引入的时候,ts没有办法进行很好的类型检测

所以此时可以添加类型声明文件

ts中文件后缀一般有2种: .ts.d.ts

.ts 文件,这些文件最终会输出 .js 文件,也是我们通常编写代码的地方

.d.ts 文件,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪 些类型, 不会被编译为js文件

类型查找分类

  • 内置类型声明 --- ts包被下载后,就已经被下载的类型声明文件,存放于typescript/lib文件夹下,用来定义ES中的一些常见对象和方法,如window,document的类型声明,具体声明文件可以在typescript项目中进行查看

  • 外部定义类型声明 --- 外部类型声明通常是我们使用一些库(比如第三方库)时,需要的一些类型声明

    外部类型声明一般有2种方式:

    • 在自己库中进行类型声明(编写.d.ts文件),比如axios

      // axios库内部自带声明描述文件,所以可以直接引入后使用
      import axios from 'axios'
      
    • 通过社区的一个公有库DefinitelyTyped存放类型声明文件

      // loadsh没有自带类型声明文件,但是在DefinitelyTyped这个社区项目中已定义
      // 此时安装lodash的类型声明文件后即可在ts中使用loadsh
      // typescript会自动进行类型查找和检测
      // npm i loadsh -S
      // npm i @types/loadsh -D
      import _ from 'lodash'
      
      // 我们可以在下边这个网址中,查看哪些模块拥有第三方的类型声明文件
      // https://www.typescriptlang.org/dt/search?search= 
      
  • 自己定义类型声明

  1. 我们使用的第三方库是一个纯的JavaScript库,没有对应的第三方声明文件
  2. 我们给自己的代码中声明一些类型,方便在其他地方直接进行使用

我们可以在项目的任意位置去放置我们的.d.ts文件,但是推荐放置在types文件夹下

index.d.ts

// d.ts文件只是完成类型声明,并不实现对应的逻辑

// 声明变量
declare const user: { name: string }

// 声明函数
declare function printInfo(user: {name: string}): void

// 声明类
declare class Person {
  name: string
  age: number
  constructor(name: string, age: number)
}

// 声明模块 --- 多用于声明全局第三方模块的属性和方法
declare module lodash {
  // 定义调用的方法
  export function join(arr: any[]): void
}

// 声明文件
declare module '*.jpg'

// 声明命名空间 --- 多用于声明全局变量的属性和方法
declare namespace $ {
  export function ajax(settings: any): any
}