快速入门TypeScript

298 阅读21分钟

TypeScript 概述

概念

TypeScript是具有类型语法的javascript,是一门强类型的编程语言。

TypeScript微软开发的开源编程语言,Type + JavaScript(type是类型 => 在 JS 基础之上,为 JS 添加了类型支持),简称:TS

代码层面

// TS 代码
// 变量age1是强类型的,有明确的类型。即: number(数值类型)
let age1: number = 18
age1 = '18' // 报错
// --------------------

// javascript代码
// 变量age2是弱类型的 无明确的类型
let age2 = 18
age2 = '18' // 不报错

带来的好处

  1. 静态类型检查,提前发现代码错误

  1. 良好的代码提示,提升开发效率

什么情况用TS而不是js

以下是一些建议

  1. 你做的是一个大型的项目?
  2. 是否是一个团队开发模式?
  3. 是否在编写通用的代码库?

结论:

ts不是万能的,技术的选项不能脱离具体的业务和应用场景,TS更加适合开发中大型的项目,或者是通用的JS代码库,再或者是团队协作开发的场景

运行第一个TS代码

安装编译 TS 的工具包

TypeScript编写的代码是无法直接在js引擎(浏览器/Nodejs)中运行的,最终还需要经过编译成js代码才可以正常运行。

带来的好处: 既可以在开发时使用TS编写代码享受类型带来的好处,同时保证运行的还是JS代码。

搭建手动编译环境

  1. 全局安装 typescript 包(编译引擎)-> 注册 tsc
    命令 npm install -g typescript
  2. 新增 hello.ts 文件, 执行 tsc hello.ts 命令生成hello.js文件
  3. 执行 node hello.js 运行js文件查看效果

搭建自动编译环境

基于工程化的TS开发模式(webpack / vite),TS的编译环境已经内置了,无需手动安装配置,通过以下命令即可创建一个最基础的自动化的TS编译环境

npm create vite@latest ts-pro -- --template vanilla-ts

在线体验版本:stackblitz.com/edit/vitejs…

命令说明:

  1. npm create vite@latest 使用最新版本的vite创建项目
  2. ts-pro 项目名称
  3. -- --template vanilla-ts 创建项目使用的模板为原生ts模板

Error Lens(vscode插件)

vscode插件商店直接搜索下载(可以提高TS代码报错的阅读性)

image.png

类型注解

概念:类型注解指的是给变量添加类型约束,它的好处是:

  1. 使变量只能被赋值为约定好的类型

  2. 编写代码的过程中可以有相关的类型提示

示例代码:

let age: number = 18

说明:代码中的 : number 就是类型注解

作用:为变量添加类型约束。比如,上述代码中,约定变量 age 的类型为 number 类型

解释:约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错

约定了类型之后,代码的提示就会非常的清晰

TS支持的常用类型注解

  • JS 已有类型
    • 原始类型,简单类型(number/string/boolean/null/undefined
    • 复杂数据类型(数组,对象,函数等)
  • TS 新增类型
    • 联合类型

    • 自定义类型(类型别名)

    • 接口

    • 元组

    • 字面量类型

    • 枚举

    • void

    • ...

简单类型如何进行类型注解

简单类型的注解完全按照 JS的类型(全小写的格式)来书写即可

类型推论

问:每个简单的变量都要写类型注解么?

let age: number = 18
let myName: string = '小花'
let isLoading: boolean = false

上面的类型注解,是不是都点多余了。

类型推论

某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型

换句话说:由于类型推论的存在,有些场合下的类型注解可以省略不写

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

  1. 声明变量并初始化时

  2. 决定函数返回值时

示例代码

// 变量 age 的类型被自动推断为:number
let age = 18

// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number) {
  return num1 + num2
}

技巧

  1. 能省略类型注解的地方就省略(偷懒,充分利用TS类型推论的能力,提升开发效率)
  2. 如果不知道类型,可以通过鼠标放在变量名称上,利用 VSCode 的提示来查看类型

数组类型注解

注解数组有什么用

变量被注解为数组类型之后,有两点好处:

  1. 不仅可以限制变量类型为数组而且可以限制数组成员的类型

  1. 编码时不仅可以提示数组的属性和方法而且可以提示成员的属性和方法

如何注解数组类型

使用数据类型对变量进行类型注解有两种语法

语法1:

上面的代码表示: 变量arr只能用来保存数组类型,并且数组的元素必须是number类型

语法2:泛型函数

联合类型

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

let arr: (number | string)[] = [1, 'a', 3, 'b']

联合类型

概念:将多个类型合并为一个类型对变量进行注解

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

注意:这是 TS 中联合类型的语法,只有一根竖线,不要与 JS 中的或(|| 或)混淆了

类型别名-type

const arr1:(number | string)[] = [1, '1'] 
const arr2:(number | string)[] = ['a', 'b'] 

如上代码中,存在类型注解重复的问题: (number | string)[] 这个类型出现了多次, 怎么优化?

类型别名

定义: type 别名 = 类型
别名可以是任意的合法字符串,一般首字母大写

例子: type MyArr = (number | string) []

作用:给类型起别名 ---> 定义了新类型

使用:

type MyArr = (number | string) [] 
const arr1:MyArr = [1, '1'] 
const arr2:MyArr = ['a', 'b'] 

图示

函数类型

基础使用

概念:函数类型是指给函数添加类型注解,本质上就是给函数的参数返回值添加类型约束

说明:

  1. 函数参数注解类型之后不但限制了参数的类型还限制了参数为必填
  2. 函数返回值注解类型之后限制了该函数内部return出去的值必须满足类型要求

好处:

  1. 避免因为参数不对导致的函数内部逻辑错误
  2. 对函数起到说明的作用

函数表达式

函数表达式的类型注解有两种方式:

  1. 参数和返回值分开注解
  2. 函数整体注解

参数和返回值分开注解

函数整体注解(只针对于函数表达式)

函数可选参数

概念:可选参数表示当前参数可传可不传,一旦传递实参必须保证参数类型正确

说明:lastName参数表示可选参数,可传可不传,一旦传递实参必须保证类型为string类型

函数无返回值 - void

概念:JS中的有些函数只有功能没有返回值,此时使用void进行返回值注解,明确表示函数没有函数值

注意事项:

  1. 在JS中如果没有返回值,默认返回的是undefined
  2. 在TS中 void和undefined不是一回事:
    1. undefined在TS中是一种明确的简单类型,如果指定返回值为undefined,那返回值必须是undefined类型
    2. void表示没有返回值

interface-接口-基本使用

作用

作用: 用interface来描述对象数据的类型(常用于给对象的属性和方法添加类型约束)

说明:一旦注解接口类型之后对象的属性和方法类型都需要满足要求,属性不能多也不能少

典型场景

场景:在常规业务开发中比较典型的就是前后端数据通信的场景

  1. 前端向后端发送数据:收集表单对象数据时的类型校验

参考: element-plus.org/zh-CN/compo…

  1. 前端使用后端数据:渲染后端对象数组列表时的智能提示

interface接口的可选设置和继承

场景

对象的某个属性是可选的。

例如:从后端取回的商品数据中,一定有id,name, price,但是imgUrl是可选的。表示有些商品没有配图片。

const goodList = [
  {id:1, name: '手机', price: 2999, imgUrl: 'http://w.g1.png'},
  {id:1, name: '毛巾', price: 9}
]

我们这么约定goodList的格式,下面的代码会报错:

interface goodItem {
    id: number;
    name: string;
    price: number;
    imgUrl: string;
} 
const goodList: goodItem[] = [
    {id:1, name: '手机', price: 2999, imgUrl: 'http://w.g1.png'},
    {id:1, name: '毛巾', price: 9}    // 这里会报错
  ]

可选设置

概念: 通过 对属性进行可选标注,赋值的时候该属性可以缺失,如果有值必须保证类型满足要求。

格式:

interface 接口名{
	属性1:类型1,
  属性2?:类型2, // 属性2是可选的
  属性3?:类型3, // 属性3是可选的
}

代码

interface goodItem {
    id: number;
    name: string;
    price: number;
    imgUrl?: string; // 可选的
} 
const goodList: goodItem[] = [
    {id:1, name: '手机', price: 2999, imgUrl: 'http://w.g1.png'},
    {id:1, name: '毛巾', price: 9}    // 这里也是正确的
]

接口的继承

概念:接口的很多属性是可以进行类型复用的,使用 extends 实现接口继承,实现类型复用。

// 正常的商品
interface goodItem {
    id: number;
    name: string;
    price: number;
    imgUrl?: string;
} 

// 打折的商品:正常商品 + newPrice + effectDate
interface  goodItemDiscount {
    id: number;
    name: string;
    price: number;
    imgUrl?: string;
    newPrice: number;
    effectDate: Date 
} 

继承之后

interface  goodItemDiscount extends goodItem {
    newPrice: number;
    effectDate: Date
} 

type注解对象类型

注解对象

概念:在TS中对于对象数据的类型注解,除了使用interface之外还可以使用类型别名来进行注解,作用相似

type + 交叉类型模拟继承

类型别名配合交叉类型(&)可以模拟继承,同样可以实现类型复用

interface 对比 type

相同点

  1. 都能描述对象类型
  2. 都能实现继承,interface使用extends, type配合交叉类型

不同点

  1. type除了能描述对象还可以用来自定义其他类型
  2. 同名的interface会合并(属性取并集,不能出现类型冲突)
  3. 同名type会报错

在注解对象类型的场景下非常相似,推荐大家使用type, type更加灵活

字面量类型

思考

以下代码,两个变量的类型分别是什么?

let str1 = 'abc'
const str2 = 'abc'

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

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

解释:

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

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

字面量类型的实际应用

字面量类型在实际应用中通常和联合类型结合起来使用,提供一个精确的可选范围

场景1:性别只能是 ’男‘ 和 ’女‘,就可以采用联合类型配合字面量的类型定义方案

场景2:ElementUI中的el-button组件按钮的type属性

any 类型(anyScript)

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

let obj: any = { x: 0 }  // any 就是任意类型

obj.bar = 100
obj()
const n: number = obj

解释:

  • 以上操作都不会有任何类型错误提示,即使可能存在错误

两种隐式 any 类型的情况

  1. 声明变量不提供类型也不提供默认值

  2. 函数参数不加类型

原则:

  1. 不推荐使用 any! 这会让 TypeScript 变为 “AnyScript”(失去 TS 类型保护的优势)
  2. 除非临时使用 any 来“避免”书写很长、很复杂的类型

类型断言

问题

获取页面中的id为link的a元素,尝试通过点语法访问href属性

发现代码报错了

原因是:getElementById方法返回值的类型是 HTMLElement,而这个类型只包含所有标签公共的属性或方法,这个类型太宽泛(不具体) ,不包含 a 标签特有的 href 等属性。

类型断言

有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言(as)来指定更具体的类型

应用场景:把一个大类型 缩小 为更加具体的类型

格式:类型 as 具体类型


使用类型断言:

解释:

  1. 使用 as 关键字实现类型断言
  2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)
  3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了

泛型

思考:后端接口给前端返回数据时,一般会遵循一定的规律,例如:都有code, msg, data三个属性,而不同的接口返回的具体数据保存在data中,它的格式是不同的。

如下是两个典型的返回结果:

// 请求得到用户信息
const res1 = {code: 200, msg: 'success', data: {name: 'jack', age: 18}}

// 请求得到商品信息
const res2 = {code: 200, msg: 'success', data: [{id: 1001, goodsName: '衬衣'}}]

提问:如何给他们提供注解?

示例代码: 定义两个不同的接口

interface Res1  {
	code: number,
  msg: string,
  data: {
    name: string,
    age: number
  }
}
interface Res2  {
	code: number,
  msg: string,
  data: {
    goodsName: string,
    id: number
  }[]
}

const res1: Res1 = {}// 
const res2: Res2 = {}// 

问题:两种类型有重复的代码。

概念

泛型(Generics)是指在定义接口、函数等类型的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性 , 使用泛型可以复用类型并且让类型更加灵活。

泛型接口

语法:在接口类型的名称后面使用<T>即可声明一个泛型参数列表,接口里的其他成员都能使用该参数的类型。

类比:函数的形参

通用思路:

  1. 找到可变的类型部分通过泛型抽象为泛型参数(定义参数)

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

  1. 在使用泛型的时候,把具体类型传入到泛型参数位置 (传参)

拓展

TS中,对js的数组做的处理:

泛型-类型别名

语法:在类型别名type的后面使用<T>即可声明一个泛型参数,接口里的其他成员都能使用该参数的类型。

需求:使用泛型别名重构ResData案例

const res1 = {code: 200, msg: 'success', data: {name: 'jack', age: 18}}
const res2 = {code: 200, msg: 'success', data: [{id: 1001, goodsName: '衬衣'}}]

参考

type User = {name: string, age: number}
type Good = {id:number, goodsName: string}
type Res<T> = {
    code: number
    msg: string
    data: T
}
type UserInfo = Res<User>
type GoodInfo = Res< Good[] >

const res1:UserInfo = {code: 200, msg: 'success', data: {name: 'jack', age: 18}}
const res2:GoodInfo = {code: 200, msg: 'success', data: [{id: 1001, goodsName: '衬衣'}] }

console.log(res1.data.)
// res1

// res1.data.age

export {}

泛型-函数

定义一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值(多种类型)

function createArray(len, initValue) {
  let result = [];
  for (let i = 0; i < length; i++) {
    result[i] = initValue
  }
  return
}
createArray(4,'a') // 得到一个长度为4的数组,它的每个元素都是'a'
createArray(3,1)   // 得到一个长度为3的数组,它的每个元素都是1

语法:在函数名称的后面使用即可声明泛型参数,整个函数中(参数返回值函数体)的变量都可以使用该参数的类型

参考代码

function createArray<T>(len: number, initValue:T) : T[] {    
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = initValue
  }
  return result
}

泛型约束

作用:泛型的特点就是灵活不确定,有些时候泛型函数的内部需要访问一些特定类型的数据才有的属性,此时会有类型错误,需要通过泛型约束解决

下面是一个报错的例子:

泛型约束的格式

<T extends 具体类型>

添加约束

结合vue3来使用TypeScript

使用vite来创建vue3+TS的项目

使用vite创建项目,并选择带ts的版本

npm create vite@latest my-vue-ts-app -- --template vue-ts

参考链接:vuejs.org/guide/types…

vue3配合ts中,还需要额外安装一个vscode插件:Typescript Vue Plugin

ref函数标注类型

通过泛型指定value的值类型:

  1. 如果是简单值,该类型可以省略,利用类型推导就可以。
  2. 如果是复杂类型,指定泛型

示例

// 1. 通过泛型指定value的值类型,如果是简单值,该类型可以省略
const money = ref<number>(10)

const money = ref(10)

// 2. 复杂类型,推荐指定泛型
type Todo = {
  id: number
  name: string
  done: boolean
}
const list = ref<Todo[]>([])

reactive函数类型

通过泛型参数的形式增加类型

<script setup lang="ts">
    import {
        reactive
    } from 'vue'

    type Person {
        name: string
        age: number | string
    }
    const p = reactive <Person> ({
        name: 'ifer',
        age: 18,
    })
</script>

<template>
    <div>
        <p>name: {{ p.name }}</p>
        <p>age: {{ p.age }}</p>
    </div>
</template>

computed函数类型

  1. 利用 TS 类型推导的能力(推荐)。

import { ref, computed } from 'vue'

const count = ref(100);
const doubleCount = computed(() => count.value * 2);
  1. 通过泛型可以指定 computed 计算属性的类型。

import { ref, computed } from 'vue'
const count = ref(100);

const doubleMoney = computed<string>(() => (count.value * 2).toFixed(2));

defineProps

之前

父传子,把props传递给子组件内部使用。可以通过defineProps来接收

defineProps配合vue默认语法进行类型校验(运行时声明)

<script setup>
// 运行时声明
const props = defineProps({
  money: {
    type: Number,
    required: true
  },
  car: {
    type: String,
    default: '小黄车'
  }
})
</script>

配合TS使用

  1. 类型校验

defineProps配合ts的泛型定义props,这样更直接

// 使用ts的泛型指令props类型
const props = defineProps<{ money: number, car?: string}>()
  1. props可以通过解构来指定默认值
<script lang="ts" setup>
// 使用ts的泛型指令props类型
const { money, car = '小黄车' } = defineProps<{
  money: number
  car?: string
}>()
</script>

注意:

!!!解构设置默认值需要额外配置

**如果提供的默认值需要在模板中渲染,需要额外添加配置**

vuejs.org/guide/extra…

// vite.config.js
export default {
  plugins: [
    vue({
      reactivityTransform: true
    })
  ]
}

defineEmits

目标

掌握defineEmit如何配合ts使用

在子传父的背景下,使用defineEmits

  1. defineEmits配合运行时声明(简单)
<script lang="ts" setup>
const emit = defineEmits(['change', 'update'])
</script>
  1. defineEmits配合ts 类型声明,可以实现更细粒度的校验

const emit = defineEmits<{
  (e: 'changeMoney', money: number): void
  (e: 'changeCar', car: string): void
}>()

事件处理

基本示例

不写任何类型,会报错,但是可以跑起来

做法

  1. 在template中,明确传入指定$event
  2. 在回调函数中指定参数的类型
const move = (e: MouseEvent) => {
  // 此时,写e. 就会有对应的提示
  mouse.value.x = e.pageX
  mouse.value.y = e.pageY
}

<h1 @mousemove="move($event)">根组件</h1>

模板引用Ref

目标

掌握ref操作DOM时如何配合Typescript使用

问题

<script setup lang="ts">
  const h1Ref = ref(null)
  const getElement = () => {
    console.log(h1Ref.value.innerHTML) // 这里会报错
  }
</script>

<tempalte>
  <div><h1 ref="h1Ref">在获取dom时通过泛型指定类型</h1>
</template>

上面的代码中,第4行会报错: 它认为h1Ref.value上没有innerHTML这个属性

ref用于获取页面中元素时,有两个状态:

  1. 初始转态 Null
  2. 绑定后的状态

改进

联合类型

const imgRef = ref<HTMLImageElement | null>(null)

onMounted(() => {
  console.log(imgRef.value?.src)
})

技巧:如何查看一个DOM对象的类型:通过控制台进行查看

document.createElement('img').__proto__

ref 操作组件

App.vue

<script setup lang="ts">
    import { ref } from 'vue'
    import Child from './Child.vue'
    
    // 通过内置的泛型工具 InstanceType 可以获取构造函数类型的实例类型
    const childCmp = ref < InstanceType < typeof Child > | null > (null)

    const handleClick = () => {
        childCmp.value?.logHello()
    }
</script>
<template>
    <section>
        <h3>App</h3>
        <button @click="handleClick">click</button>
        <hr />
        <Child ref="childCmp" />
    </section>
</template>

Child.vue

<script setup lang="ts">
    const logHello = () => {
        console.log('🤣')
    }
    defineExpose({
        logHello,
    })
</script>
<template>
    <div>Child</div>
</template>

可选链操作符

目标:掌握 JS 中的提供的可选链操作符语法。

可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效,参考文档

const nestedProp = obj.first?.second
// 等价于
let temp = obj.first
let nestedProp = temp === null || temp === undefined ? undefined : temp.second
// 旧写法
if (obj.fn) {
    obj.fn()
}
obj.fn && obj.fn()
// 可选链
obj.fn?.()

非空断言

目标:掌握 TS 中的非空断言的使用语法。

  • 如果我们明确的知道对象的属性一定不会为空,那么可以使用非空断言 !

// 告诉 TS, 明确的指定 obj 不可能为空
const nestedProp = obj!.second

// 表示 document.querySelector('div') 不可能为空
console.log(document.querySelector('div')!.innerHTML)
  • 注意:非空断言一定要确保有该属性才能使用,不然使用非空断言会导致 Bug。

TypeScript类型声明文件

背景

几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。
这些第三方库不管是否是用 TS 编写的,最终都要编译成 JS 代码,才能发布给开发者使用。

我们知道是 TS 提供了类型,才有了代码提示和类型保护等机制。

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

类型声明文件

用来为已存在的 JS 库提供类型信息

TS 中有两种文件类型:

1 .ts 文件

2 .d.ts 文件

.ts 文件:

  1. 既包含类型信息又可执行代码
    1. 可以被编译为 .js 文件,然后,执行代码
    2. 用途:编写程序代码的地方

.d.ts 文件:

  1. 只包含类型信息的类型声明文件
  2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
  3. 第三方的声明文件(需要安装),一般会跟随第三方包一起安装,支持ts的,都会自带;
  4. 用途:为 JS 提供类型信息

总结

.ts 是 implementation(代码实现文件); .d.ts 是 declaration(类型声明文件)

如果要为 JS 库提供类型信息,要使用 .d.ts 文件

内置类型声明文件

  • TS 为 JS 运行时可用的所有标准化内置 API 都提供了声明文件

  • 比如,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:

const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach
  • 实际上这都是 TS 提供的内置类型声明文件

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

  • 比如,查看 forEach 方法的类型声明,在 VSCode 中会自动跳转到 lib.es5.d.ts 类型声明文件中

  • 当然,像 window、document 等 BOM、DOM API 也都有相应的类型声明(lib.dom.d.ts)

第三方库类型声明文件

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

第三方库的类型声明文件有两种存在形式:1 库自带类型声明文件 2 由 TS官方给它写的(DefinitelyTyped 提供)。

  1. 库自带类型声明文件:比如,axios
    • 查看 node_modules/axios 目录

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

  1. 由 DefinitelyTyped 提供

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

  • DefinitelyTyped 链接

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

  • 比如,@types/react、@types/lodash 等

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

import _ from 'lodash'

// 在 VSCode 中,查看 'lodash' 前面的提示
  • 解释:当安装 @types/* 类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明
  • 补充:TS 官方文档提供了一个页面,可以来查询 @types/* 库
  • @types/* 库

类型声明-自定义的文件

如下两种场景需要提供类型声明文件

  1. 项目内共享类型
  2. 为已有 JS 文件提供类型声明

项目内共享类型

文件目录:

a.ts
b.ts

文件内容:

改进

将公共的类型定义提取出来,写在index.d.ts文件中

index.d.ts
a.ts
b.ts

定义接口,并导出

export interface Token {
  token: string
  refresh_token: string
}

a.js

导入接口并使用

import { Token } from './index' // 必须省略.d.ts

function showToken(token: Token)

小结

  1. 如果多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型声明
  2. 步骤:
    1. 创建 index.d.ts 类型声明文件。
    2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
    3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

类型声明-自定义的文件-为已有 JS 文件提供类型声明

背景

有一个ts项目,用到了.js文件,此时项目中ts和js同时存在,且不想将.js改成.ts

demo.ts
utils/index.js

utils/index.js

let songName = '痴心绝对'

function add(x, y) {
  return x + y
}

const fomartPoint = point => {
  console.log('当前坐标:', point)
}

export { add, songName }

demo.ts

import { add } from './utils'  // 这里可以使用,但是,没有提示。

修改一下配置文件,让当前项目支持.js文件的引入

这里可以使用,但是,没有提示!

如何基于现有的.js提供声明文件?

思路:编写同名的.d.ts文件

demo.ts
utils/index.js
utils/index.d.ts // 这里是重点

定义类型声明文件

  1. 它的作用是提供声明,不需要提供逻辑代码;
  2. declare 关键字:用于类型声明,为其他地方(比如,.js 文件)已存在的变量声明类型,而不是创建一个新的变量。
    1. 对于 type、interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
    2. 对于 let、function 等具有双重含义(在 JS、TS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。
declare let add: string

declare function add (x :number, y: number) : number

export {
  songName, add
}

Axios与Typescript

缺少提示

const getChannel = async () => {

  const res = await axios.get<{data:{channels: []}}>('http://geek.itheima.net/v1_0/channels')
  console.log(res.data.data.channels)
}

getChannel()

解决方案

type ChannelRes = {
  data: {channels: {id: number, name: string} []},
  message: string
}
const getChannel = async () => {

  const res = await axios.get<ChannelRes>('http://geek.itheima.net/v1_0/channels')
  console.log(res.data.data.channels)
}

getChannel()