文章目录

  • 🧑‍💻TypeScript基本概念
    • TypeScript 是什么?
    • 为什么要有typescript
    • 安装编译 TS 的工具包
    • 编译并运行 TS 代码
    • 创建基于TS的vue项目
  • 🧑‍💻TypeScript基础
    • 类型注解
    • TypeScript类型概述
    • TypeScript原始数据类型
    • 数组类型
    • 联合类型
    • 类型别名
    • 函数类型
      • 基本使用
      • void 类型
      • 可选参数
    • 对象类型
      • 基本使用
      • 箭头函数形式的方法类型
      • 对象可选属性
      • 使用类型别名
      • 练习
    • 接口类型
      • 基本使用
      • interface vs type
      • 接口继承
    • 元组类型
    • 类型推论
    • 字面量类型
      • 基本使用
      • 使用模式和场景
    • 枚举类型
      • 基本使用
      • 数字枚举
      • 字符串枚举
      • 枚举实现原理
    • any 类型
    • 类型断言
  • 🧑‍💻TypeScript泛型
    • 泛型-基本介绍
    • 泛型-泛型函数
    • 简化泛型函数调用
    • 泛型约束
    • 指定更加具体的类型
    • 添加约束
    • 多个类型变量
    • 泛型接口
    • JS 中的泛型接口
  • 🧑‍💻TypeScript与Vue
    • defineProps与Typescript
    • defineEmits与Typescript
    • ref与Typescript
    • reactive与Typescript
    • computed与Typescript
    • 事件处理与Typescript
    • Template Ref与Typescript
    • 可选链操作符
    • 非空断言
  • 🧑‍💻TypeScript类型声明文件
    • 基本介绍
    • 内置类型声明文件
    • 第三方库类型声明文件
    • 自定义类型声明文件-共享数据

🧑‍💻TypeScript基本概念

TypeScript 是什么?

目标:能够说出什么是typescript

内容:

【TypeScript】TS 看这一篇就够了

【TypeScript】TS 看这一篇就够了

【TypeScript】TS 看这一篇就够了

为什么要有typescript

目标:能够说出为什么需要有typescript

内容:

为什么会这样? var num = 18 num.toLowerCase()

并且,配合 VSCode 等开发工具,TS 可以提前到在编写代码的同时就发现代码中的错误,减少找 Bug、改 Bug 时间

对比:

Vue 3 源码使用 TS 重写、Angular 默认支持 TS、React 与 TS 完美配合,TypeScript 已成为大中型前端 项目的首选编程语言

目前,前端最新的开发技术栈:

  1. React: TS + Hooks
  2. Vue: TS + Vue3

安装编译 TS 的工具包

目标:能够安装ts的工具包来编译ts

内容:

【TypeScript】TS 看这一篇就够了

编译并运行 TS 代码

目标:能够理解typescript的运行步骤

内容:

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

1 创建 ts 文件 ===> 2 编译 TS ===> 3 执行 JS

真正在开发过程中,其实不需要自己手动的通过tsc把ts文件转成js文件,这些工作应该交给webpack或者vite来完成

创建基于TS的vue项目

目标:能够使用vite创建vue-ts模板的项目

内容:

基于vite创建一个vue项目,使用typescript模板

yarn create vite vite-ts-demo  --template vue-ts

🧑‍💻TypeScript基础

类型注解

目标:能够理解什么是typescript的类型注解

内容:

示例代码:

let age = 18
let age: number = 18
// 错误代码:
// 错误原因:将 string 类型的值赋值给了 number 类型的变量,类型不一致
let age: number = '18'

TypeScript类型概述

目标:能够理解TypeScript中有哪些数据类型

内容:

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

TypeScript原始数据类型

let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false
// 等等...

数组类型

目标:掌握ts中数组类型的两种写法

内容:

// 写法一:
let numbers: number[] = [1, 3, 5]
// 写法二:
let strings: Array<string> = ['a', 'b', 'c']

联合类型

目标:能够通过联合类型将多个类型组合成一个类型

内容:

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

let arr: (number | string)[] = [1, 'a', 3, 'b']
  let timer: number | null = null
  timer = setInterval(() => {}, 1000)
  // 定义一个数组,数组中可以有数字或者字符串, 需要注意 | 的优先级
  let arr: (number | string)[] = [1, 'abc', 2]

类型别名

目标:能够使用类型别名给类型起别名

内容:

type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]

函数类型

基本使用

目标:能够给函数指定类型

内容:

  1. 单独指定参数、返回值的类型:
// 函数声明
function add(num1: number, num2: number): number {
  return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
  return num1 + num2
}
  1. 同时指定参数、返回值的类型:
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
  return num1 + num2
}

void 类型

目标:能够了解void类型的使用

内容:

function greet(name: string): void {
  console.log('Hello', name)
}
// 如果什么都不写,此时,add 函数的返回值类型为: void
const add = () => {}
// 这种写法是明确指定函数返回值类型为 void,与上面不指定返回值类型相同
const add = (): void => {}
// 但,如果指定 返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
const add = (): undefined => {
  // 此处,返回的 undefined 是 JS 中的一个值
  return undefined
}

可选参数

目标:能够使用?给函数指令可选参数类型

内容:

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

对象类型

基本使用

目标:掌握对象类型的基本使用

内容:

// 空对象
let person: {} = {}
// 有属性的对象
let person: { name: string } = {
  name: '同学'
}
// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
  name: 'jack',
  sayHi() {}
}
// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
  name: string
  sayHi(): void
} = {
  name: 'jack',
  sayHi() {}
}
// 练习
指定学生的类型
姓名
性别
成绩
身高
学习
打游戏

箭头函数形式的方法类型

{
    greet(name: string):string,
    greet: (name: string) => string
}
type Person = {
  greet: (name: string) => void
  greet(name: string):void
}
let person: Person = {
  greet(name) {
    console.log(name)
  }
}

对象可选属性

type Config = {
  url: string
  method?: string
}
function myAxios(config: Config) {
  console.log(config)
}

使用类型别名

// 创建类型别名
type Person = {
  name: string
  sayHi(): void
}
// 使用类型别名作为对象的类型:
let person: Person = {
  name: 'jack',
  sayHi() {}
}

练习

创建两个对象:
学生对象
指定对象的类型
姓名
性别
成绩
身高
学习
打游戏

接口类型

基本使用

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

interface IPerson {
  name: string
  age: number
  sayHi(): void
}
let person: IPerson = {
  name: 'jack',
  age: 19,
  sayHi() {}
}

interface vs type

interface IPerson {
  name: string
  age: number
  sayHi(): void
}
// 为对象类型创建类型别名
type IPerson = {
  name: string
  age: number
  sayHi(): void
}
// 为联合类型创建类型别名
type NumStr = number | string

接口继承

interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
  z: number
}

元组类型

let position: number[] = [116.2317, 39.5427]
let position: [number, number] = [39.5427, 116.2317]

类型推论

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

字面量类型

基本使用

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

使用模式和场景

// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {
  console.log(direction)
}
// 调用函数时,会有类型提示:
changeDirection('up')

枚举类型

基本使用

// 创建枚举
enum Direction { Up, Down, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
  console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Up)

数字枚举

// Down -> 11、Left -> 12、Right -> 13
enum Direction { Up = 10, Down, Left, Right }
enum Direction { Up = 2, Down = 4, Left = 8, Right = 16 }

字符串枚举

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

枚举实现原理

enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT'
}
// 会被编译为以下 JS 代码:
var Direction;
(function (Direction) {
  Direction['Up'] = 'UP'
  Direction['Down'] = 'DOWN'
  Direction['Left'] = 'LEFT'
  Direction['Right'] = 'RIGHT'
})(Direction || Direction = {})

any 类型

let obj: any = { x: 0 }
obj.bar = 100
obj()
const n: number = obj

类型断言

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

const aLink = document.getElementById('link')
const aLink = document.getElementById('link') as HTMLAnchorElement
// 该语法,知道即可:
const aLink = <HTMLAnchorElement>document.getElementById('link')

🧑‍💻TypeScript泛型

泛型-基本介绍

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

泛型-泛型函数

定义泛型函数

function id<Type>(value: Type): Type { return value }
function id<T>(value: T): T { return value }

调用泛型函数

const num = id<number>(10)
const str = id<string>('a')

简化泛型函数调用

// 省略 <number> 调用函数
let num = id(10)
let str = id('a')

泛型约束

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

指定更加具体的类型

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

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

添加约束

// 创建一个接口
interface ILength { length: number }
// Type extends ILength 添加泛型约束
// 解释:表示传入的 类型 必须满足 ILength 接口的要求才行,也就是得有一个 number 类型的 length 属性
function id<Type extends ILength>(value: Type): Type {
  console.log(value.length)
  return value
}

多个类型变量

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

function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key]
}
let person = { name: 'jack', age: 18 }
getProp(person, 'name')
// Type extends object 表示: Type 应该是一个对象类型,如果不是 对象 类型,就会报错
// 如果要用到 对象 类型,应该用 object ,而不是 Object
function getProperty<Type extends object, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key]
}

泛型接口

泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强其复用性

interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}
let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}

JS 中的泛型接口

实际上,JS 中的数组在 TS 中就是一个泛型接口。

const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach
const nums = [1, 3, 5]
// 鼠标放在 forEach 上查看类型
nums.forEach

🧑‍💻TypeScript与Vue

参考链接:https://vuejs.org/guide/typescript/composition-api.html

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

【TypeScript】TS 看这一篇就够了

defineProps与Typescript

目标:掌握defineProps如何配合ts使用

  1. defineProps配合vue默认语法进行类型校验(运行时声明)
// 运行时声明
defineProps({
  money: {
    type: Number,
    required: true
  },
  car: {
    type: String,
    required: true
  }
})
  1. defineProps配合ts的泛型定义props类型校验,这样更直接
// 使用ts的泛型指令props类型
defineProps<{
  money: number
  car?: string
}>()
  1. props可以通过解构来指定默认值
<script lang="ts" setup>
// 使用ts的泛型指令props类型
const { money, car = '小黄车' } = defineProps<{
  money: number
  car?: string
}>()
</script>

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

https://vuejs.org/guide/extras/reactivity-transform.html#explicit-opt-in

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

defineEmits与Typescript

目标:掌握defineEmit如何配合ts使用

  1. defineEmits配合运行时声明
const emit = defineEmits(['change', 'update'])
  1. defineEmits配合ts 类型声明,可以实现更细粒度的校验
const emit = defineEmits<{
  (e: 'changeMoney', money: number): void
  (e: 'changeCar', car: string): void
}>()

ref与Typescript

目标:掌握ref配合ts如何使用

  1. 通过泛型指定value的值类型,如果是简单值,该类型可以省略
const money = ref<number>(10)
const money = ref(10)
  1. 如果是复杂类型,推荐指定泛型
type Todo = {
  id: number
  name: string
  done: boolean
}
const list = ref<Todo[]>([])
setTimeout(() => {
  list.value = [
    { id: 1, name: '吃饭', done: false },
    { id: 2, name: '睡觉', done: true }
  ]
})

reactive与Typescript

目标:掌握reactive配合typescript如何使用

// reactive 适合于明确属性的对象场景
type User = {
  name: string
  age: number
};
const obj: User = reactive({
  name: "zs",
  age: 18
});

computed与Typescript

目标:掌握computed配合typescript如何使用

  1. 通过泛型可以指定computed计算属性的类型,通常可以省略
const leftCount = computed<number>(() => {
  return list.value.filter((item) => item.done).length
})
console.log(leftCount.value)

事件处理与Typescript

目标:掌握事件处理函数配合typescript如何使用

const move = (e: MouseEvent) => {
  mouse.value.x = e.pageX
  mouse.value.y = e.pageY
}
<h1 @mousemove="move($event)">根组件</h1>

Template Ref与Typescript

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

const imgRef = ref<HTMLImageElement | null>(null)
onMounted(() => {
  console.log(imgRef.value?.src)
})

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

document.createElement('img').__proto__

可选链操作符

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

内容

let nestedProp = obj.first?.second;
console.log(res.data?.data)
obj.fn?.()
if (obj.fn) {
    obj.fn()
}
obj.fn && obj.fn()
// 等价于
let temp = obj.first;
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);

非空断言

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

内容:

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

🧑‍💻TypeScript类型声明文件

基本介绍

今天几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。
这些第三方库不管是否是用 TS 编写的,最终都要编译成 JS 代码,才能发布给开发者使用。
我们知道是 TS 提供了类型,才有了代码提示和类型保护等机制。

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

  1. 既包含类型信息又可执行代码
  2. 可以被编译为 .js 文件,然后,执行代码
  3. 用途:编写程序代码的地方
  1. 只包含类型信息的类型声明文件
  2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
  3. 用途:为 JS 提供类型信息

内置类型声明文件

const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach

第三方库类型声明文件

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

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

  1. 由 DefinitelyTyped 提供
import _ from 'lodash'
// 在 VSCode 中,查看 'lodash' 前面的提示

自定义类型声明文件-共享数据

项目内共享类型

// a.ts
import { Props } from './index'  //通过import导入
// type Props = { x: number; y: number }
let p1: Props = {
  x: 1,
  y: 2
}
// b.ts
import { Props } from './index'  //通过import导入
// type Props = { x: number; y: number } 
let p2: Props = {
  x: 12,
  y: 24
}
// index.d.ts
type Props = { x: number; y: number }
export { Props }  //创建需要共享的类型,并使用export导出
  1. 创建 index.d.ts 类型声明文件。
  2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
  3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

自定义类型声明文件-为js提供声明

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

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

类型声明文件的使用说明

  1. 对于 type、interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
  2. 对于 let、function 等具有双重含义(在 JS、TS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。
// index.ts文件
//导入.js文件时,自动加载.js文件的类型声明文件.d.ts,然后就能用到声明好的类型了
import { count, songName, add, Point} from './utils'
type Person = {
  name: string
  age: number
}
let p:Partial<Person> = {
  name: 'itpeilibo'
}
let p1: Point = {
  x: 10,
  y: 20
}
console.log('项目启动了')
console.log('count', count)
console.log('songName', songName)
console.log('add()', add(1, 4))
//utils.js文件
let count = 10
let songName = '巨人的小脚丫'
let position = {
  x: 0,
  y: 0
}
//函数声明形式
function add(x, y) {
  return x + y
}
function changeDirection(direction) {
  console.log(direction)
}
//函数表达式形式
const fomartPoint = point => {
  console.log('当前坐标:', point)
}
export { count, songName, position, add, changeDirection, fomartPoint }

定义类型声明文件

//utils.d.ts文件
//为utils.js文件来提供类型声明
declare let count:number //为已存在的变量声明类型
declare let songName: string
interface Position {
  x: number,
  y: number
}
declare let position: Position
declare function add (x :number, y: number) : number
type Direction = 'left' | 'right' | 'top' | 'bottom'
declare function changeDirection (direction: Direction): void
//自定义函数类型
type FomartPoint = (point: Position) => void
declare const fomartPoint: FomartPoint
export {
  count, songName, position, add, changeDirection, FomartPoint, fomartPoint
}

发表回复