Vue3+TS TypeScript知识点

TypeScript起步

1.TypeScript 介绍

官方网站:https://www.typescriptlang.org/
简介
TypeScript 是一种带有 类型语法 的 JavaScript 语言,在任何使用 JavaScript 的开发场景中都可以使用。

在这里插入图片描述

  • TS 是 JS 的超集,支持了JS 语法和扩展了类型语法
  • TS 需要编译才能在浏览器运行。

JavaScript 代码

// 没有明确的类型
let age = 18

TypeScript 代码

// 有明确的类型,可以指定age是number类型(数值类型)
let age: number = 18

2.TypeScript 作用

场景
在程序运行的时候有时候会出现 Uncaught TypeError 的错误

const num = 18;
num.toLowerCase() 
// Uncaught TypeError: num.toLowerCase is not a function

在开发项目的时候发生错误,要花很多时间去定位和处理BUG
原因
JS 是动态类型的编程语言,动态类型最大的特点就是它只能在代码执行期间做类型的相关检查。
解决方案

  • TS 是静态类型的编程语言,代码会先进行编译然后去执行,在 代码编译 期间做类型的相关检查,如果有问题编译是不通过的,也就暴露出了问题。
  • 配合 VSCode 等开发工具,TS 可以提前到在 编写代码 的时候就能发现问题,更准确更快的处理错误。
    TS相比JS优势
  • 更早发现错误,提高开发效率
  • 随时随地提示,增强开发体验
  • 强大类型系统,代码可维护性更好,重构代码更容易
  • 类型推断机制,减少不必要类型注解,让编码更简单
  • Vue3源码TS重写,React和TS完美配合,Angular默认支持TS,大中型前端项目首选。

3.TypeScript 编译

安装

# npm 安装
npm i -g typescript
# yarn 安装
yarn global add typescript
# 部分mac电脑安装需要sudo权限
# sudo npm i -g typescript
# sudo yarn global add typescript

查看版本

tsc -v

编译 TS
1.创建TS文件
新建 hello.ts 文件
在这里插入图片描述
在这里插入图片描述

2.运行 TypeScript 编译器
当前目录打开命令行窗口,执行 tsc hello.ts 命令,同级目录生成 hello.js 文件
在这里插入图片描述
在这里插入图片描述

3.执行生成的 JavaScript 代码
执行 node hello.js 验证一下
在这里插入图片描述

在开发中一般使用 webpack vite 等工具自动构建编译。

创建 vue-ts 项目
使用Vite创建个基于 ts 的 vue 项目

# npm 6.x
npm create vite@latest my-vue-ts-app --template vue-ts

# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-ts-app -- --template vue-ts

# yarn
yarn create vite my-vue-ts-app --template vue-ts

# pnpm
pnpm create vite my-vue-ts-app --template vue-ts

TypeScript 核心

1.类型注解

类型注解(Type Annotations)是在代码中显式声明变量、函数参数或函数返回值的类型的方式。
作用
在 TypeScript 中,使用类型注解可以明确指定变量或函数的期望类型,从而提高代码的可读性和可维护性。
示例

let age: number;
let age: number = 18;
let name: string;
let isStudent: boolean;

  • : number 就是类型注解,它为变量提供类型约束。
  • 约定了什么类型,就只能给该变量赋值什么类型的值,否则报错。
  • 约定类型之后,代码的提示也会非常清晰。
    错误示例
let age: number = 18;
// 报错:不能将类型“string”分配给类型“number”
age = '19';

在这里插入图片描述

2.原始类型

原始类型是指最基本的数据类型
TS 常用类型

  • JS 已有类型
    简单类型:number string boolean null undefined
    复杂类型:对象 数组 函数
  • TS 新增类型
    联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any、泛型 等

示例

// number: 用于表示数值
let count: number = 10;
// string: 用于表示文本字符串
let message: string = "Hello, TypeScript!";
// boolean: 用于表示布尔值
let isDone: boolean = false;
// null:空值 undefined:未定义值。
let n: null = null;
let u: undefined = undefined;
...

3.数组类型

示例
写法一

let numbers: number[] = [1, 3, 5];

写法二

let strings: Array<string> = ['a', 'b', 'c'];

4.联合类型

联合类型(Union Types)是 TypeScript 中一种用于表示变量可以具有多个可能类型的方式。
示例

// 数组中有 number 和 string 类型
let arr: (number | string)[] = [1, 'a', 3, 'b'];
// 变量arr可以数字类型或者全是字符串的数组
let arr: number | string[];

5.类型别名 Type

类型别名(Type Aliases)是 TypeScript 中一种用于给类型起别名的机制
支持对象类型其他类型

基本使用

type 类型别名 = 具体类型

示例

// let arr: ( number | string )[] = [ 1, 'a', 4]

// 类型别名: type 类型别名 = 具体类型
type CustomArr = (number | string)[];
let arr: CustomArr = [1, 'a', 4];

// 多次复用 简化该类型的使用
type CustomArr = (number | string)[];
let arr: CustomArr = [1, 'a', 4];
let arr2: CustomArr = [2, 'b', 8];

定义类型别名,遵循大驼峰命名规范,类似于变量
使用类型别名,与类型注解的写法一样即可

type 交叉类型

交叉类型(Intersection Types)是一种将多个类型组合成一个新类型的方式。

// 使用 type 来定义 Point2D 和 Point3D
type Point2D = {
  x: number;
  y: number;
};

// 使用 交叉类型 来实现接口继承的功能:
// 使用 交叉类型 后,Point3D === { x: number; y: number; z: number }
type Point3D = Point2D & {
  z: number;
};

let o: Point3D = {
  x: 1,
  y: 2,
  z: 3,
};

6.函数类型

基本用法

在 TypeScript 中,函数类型就是给函数指定类型,
是给 参数返回值 指定类型。
示例
写法一
在函数基础上 分别指定 参数和返回值类型

// 函数声明
function add(num1: number, num2: number): number {
  return num1 + num2;
}

// 箭头函数
const add = (num1: number, num2: number): number => {
  return num1 + num2;
};

写法二
使用类型别名 同时指定 参数和返回值类型

type AddFn = (num1: number, num2: number) => number;

const add: AddFn = (num1, num2) => {
  return num1 + num2;
};

注意
通过类似箭头函数形式的语法来为函数添加类型,只适用于 函数表达式

void 类型

void 类型用于表示函数没有返回值
示例
如果函数没有返回值,定义函数类型时返回值类型为 void

const say = (): void => {
  console.log('hi');
};

如果函数没有返回值,且没有定义函数返回值类型的时候,默认是 void

const say = () => {
  console.log('hi');
};

注意

  • JS 中如果没有返回值,默认返回的是 undefined
  • TS 中如果没有返回值,TypeScript 会推断函数的返回类型为 void
  • 如果指定返回值类型是 undefined 那返回值必须是 undefined(在TS5.1版本可以不返回undefined)
const add = (): undefined => {
  return undefined;
};

可选参数

如果函数的参数,可以传也可以不传,这种情况就可以使用 可选参数 语法,参数后加 ? 即可
语法
属性?:类型
示例

const fn = (n?: number) => {
  // ..
};
fn();
fn(10);

const mySlice = (start?: number, end?: number) => {
  console.log('起始Index:', start, '结束Index:', end);
};
mySlice();
mySlice(1);
mySlice(1, 2);

注意
必选参数不能位于可选参数后
例如 (start?: number, end: number) 这样是不行的

7.对象类型

TS 的对象类型,其实就是描述对象中的 属性 方法 的类型

基本用法

语法
对象名:{属性名:类型;方法名():返回值类型}
示例

// 空对象
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() {},
};

高级用法

函数使用箭头函数类型

let person: {
  name: string
  sayHi: () => void
} = {
  name: 'jack',
  sayHi() {},
};

对象属性可选

// 例如:axios({url,method}) 如果是 get 请求 method 可以省略
const axios = (config: { url: string; method?: string }) => {};

使用类型别名

// {} 会降低代码可阅读性,建议对象使用类型别名
// const axios = (config: { url: string; method?: string }) => {};

// 改造后
type Config = {
  url: string;
  method?: string;
};
const axios = (config: Config) => {};

8.接口 interface

基本使用

接口声明是命名对象类型的另一种方式
只能支持对象类型
语法
interface 接口名{属性名:类型;方法名:()=>类型;...}
示例

// 通过interface定义对象类型
interface Person {
  name: string;
  age: number;
  sayHi: () => void;
}
// 使用类型
let person: Person = {
  name: 'jack',
  age: 19,
  sayHi() {},
};

注意
接口的每一行只能有 一个 属性或方法,每一行不需要加分号

interface 继承

相同的属性或展示可以抽离出来,然后使用 extends 实现继承复用
示例
有两个接口,有相同的属性或者函数

// 继承前
interface user1 {
  name: string;
  age: number;
}
interface user2 {
  name: string;
  age: number;
  id: number;
}

相同的属性或展示可以抽离出来,然后使用 extends 实现继承复用

interface user1 {
 name: string;
 age: number;
}
// 继承 user1
interface user2 extends user1 {
  id: number;
}
// 继承后 user2 的结构:{ name: string;age: number;id: number; }

继承后 接口A 拥有 接口B 的所有属性和函数的类型声明

9.interface 与 type

  • 类型别名和接口非常相似,在许多情况下,可以在它们之间自由选择
  • 接口的几乎所有特性都以类型的形式可用,关键的区别在于不能重新打开类型以添加新属性,而接口总是可扩展的。

区别

interfacetype
支持:对象类型支持:对象类型,其他类型
复用:可以继承复用:交叉类型

type 不可重复定义

type Person = {
  name: string;
};
// 标识符“Person”重复  Error
type Person = {
  age: number;
};

interface 重复定义会合并

interface Person {
  name: string;
}
interface Person {
  age: number;
}
// 类型会合并,注意:属性类型和方法类型不能重复定义
const p: Person = {
  name: '鲁迅',
  age: 44,
};

10.类型推断

类型推断(Type Inference)是 TypeScript 中的一个重要特性,它允许编译器在某些情况下自动推断变量的类型,而无需显式地指定类型注解。
发生类型推断的几个场景场景
声明变量并初始化时

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

决定函数返回值时

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

建议

  • 在开发项目的时候,能省略类型注解的地方就省略,充分利用TS推断 的能力,提高开发效率。
  • 如果还没有熟悉 ts 类型的时候建议都加上类型
  • 如果不知道类型怎么写,可以把鼠标放至变量上,可以通过 Vscode 提示看到类型

11.字面量类型

字面量类型是一种特殊的类型,它表示一个具体的值
字面量类型介绍

  • js 字面量如:18 'jack' ['a'] {age: 10} 等等。
  • 使用 js字面量 作为变量类型,这种类型就是字面量类型
// : 'jack' 是字面量类型
let name: 'jack' = 'jack';
// : 18 是字面量类型
let age: 18 = 18;

// 报错:不能将类型“19”分配给类型“18”
age = 19;

let str1 = 'Hello TS';
// str1 类型是string
const str2 = 'Hello TS';
// str2 类型是 Hello TS
// str2 是 const 声明的,值只能是 Hello TS,所以类型只能是 Hello TS

字面量类型应用

// 性别只能是 男 和 女,不会出现其他值。

// let gender = '男'
// gender = '女'
// ------------------------
type Gender = '男' | '女'
let gender: Gender = '男'
gender = '女'

// 字面量类型配合联合类型来使用(一组明确的可选的值)

// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'

function changeDirection(direction: Direction) {
  console.log(direction)
}

// 调用函数时,会有类型提示:
changeDirection('up')

12.any 类型

any类型会使TypeScript 编译器将对该变量的类型进行宽松的检查,即不会强制执行类型检查,允许你在编写代码时绕过 TypeScript 的类型系统。
示例
显式any情况
当变量的类型指定为 any 的时候,不会有任何错误,也不会有代码提示,TS会忽略类型检查

let obj: any = { age: 18 }
obj.bar = 100
obj()
const n: number = obj

隐式any的情况
声明变量不给类型或初始值,函数参数不给类型或初始值

// 声明变量不给类型或初始值
let a;
// 函数参数不给类型或初始值
const fn = (n) => {}

any 的使用越多,程序可能出现的漏洞越多,因此不推荐使用 any 类型,尽量避免使用。

13.类型断言

类型断言是一种方式,通过它你可以告诉编译器某个值的实际类型。这在你比编译器更清楚某个值的类型时是很有用的。
用法
使用 as 关键字实现类型断言
示例

// aLink 的类型 HTMLElement,该类型只包含所有标签公共的属性或方法
// 这个类型太宽泛,没包含 a 元素特有的属性或方法,如 href
const aLink = document.getElementById('link')

但是我们明确知道获取的是一个 A 元素,可以通过 类型断言 给它指定一个更具体的类型。

const aLink = document.getElementById('link') as HTMLAnchorElement

解释

  • 关键字 as后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型
  • 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了
const img = document.getElementById('img') as HTMLImageElement
// 如果不知道标签的类型:document.querySelector('div') 鼠标摸上去就可以看见

14.泛型

在TypeScript中,泛型是一种使代码更灵活且能够适用于不同类型的工具。使用泛型,你可以编写能够与多种类型一起工作的函数、类、接口等。

  • 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
  • 在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。

泛型别名

特点

  • 泛型:定义类型别名后加上<类型参数> 就是泛型语法, 使用的时候传入具体的类型即可
  • <T> 是一个变量,可以随意命名,建议遵循大驼峰即可。
  • 和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
  • 泛型可以提高类型的复用性灵活性
    示例
// 对后台返回的数据进行类型定义
type User = {
  name: string;
  age: number;
}

type Goods = {
  id: number;
  goodsName: string;
}

type Data<T> = {
  msg: string;
  code: number;
  data: T
}

// 使用类型
type UserData = Data<User>
type GoodsData = Data<Goods>

泛型接口

在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口,接口中所有成员都可以使用类型变量。
示例

// 对象,获取单个ID函数,获取所有ID函数,ID的类型肯定是一致的,但是可能是数字可能是字符串
interface IdFn<T> {
  id: () => T;
  ids: () => T[];
}

const idObj: IdFn<number> = {
  id() { return 1 },
  ids() { return [1, 2] },
};

内置的泛型接口

// 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键) 去查看内置的泛型接口
const arr = [1, 2, 3];
// TS有自动类型推断,其实可以看做:const arr: Array<number> = [1, 2, 3]
arr.push(4);
arr.forEach((item) => console.log(item));

泛型函数

泛型函数是使用泛型来增加函数的灵活性,使函数能够适用于多种类型的数据。
语法
函数名称后加上 <T>

  • T是类型参数,是个类型变量,命名建议遵循大驼峰即可。
  • 当你调用函数的时候,传入具体的类型,T 或捕获到这个类型,函数任何位置均可使用。
// 函数的参数是什么类型,返回值就是什么类型
function getId<T>(id: T): T {
  return id
}

let id1 = getId<number>(1)
let id2 = getId('2')
// TS会进行类型推断,参数的类型作为泛型的类型 getId<string>('2')

传入的数据可以推断出你想要的类型,就可以省略。

// 我需要的类型 { name: string, age?: number } 但是推断出来是 { name: string}
let id2 = getId({name:'jack'})

Vue中的TypeScript

typescript 配合 Vue3 composition-api 使用
https://staging-cn.vuejs.org/guide/typescript/composition-api.html

1.使用TS

script 加上 lang=“ts” 才能写ts代码

<script setup lang="ts">
...
</script>

2.defineProps的TS写法

defineProps的基本写法

const props = defineProps({
  money: {
    type: Number,
    required: true
  },
  car: {
    type: String,
    required: false,
    default: '宝马车'
  }
})
console.log(props.money) // number
console.log(props.car) // string | undefined

defineProps的TS写法
1.defineProps 通过泛型参数来定义 props 的类型通常更直接

const props = defineProps<{
  money: number
  car?: string
}>()

2.如果需要给 props 设置默认值,需要使用 withDefaults 函数

const props = withDefaults(defineProps<{
  money: number;
  car?: string;
}>(),{
  car: '宝马车'
})

3. 响应式语法糖 解构 + defineProps

响应性语法糖曾经是一个实验性功能,且已在3.4 版本中被移除
已移除的实验性功能

显式地选择开启响应式语法糖

// vite.config.ts
export default defineConfig({
  plugins: [
    vue({
      reactivityTransform: true,
    }),
  ],
});
const { money, car = "宝马车" } = defineProps<{
  money: number
  car?: string
}>();

3.defineEmits的TS写法

基本写法

const emit = defineEmits(['changeMoney', 'changeCar'])

defineEmits 通过泛型参数来定义

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

4.ref的TS写法

ref() 会隐式的依据数据推导类型

简单数据类型推荐类型推导

const money = ref<number>(10)
// 类型推导
const money = ref(10)

复杂类型推荐指定泛型

复杂数据一般是后台返回数据,默认值是空,无法进行类型推导。

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 }
  ]
}, 1000)

5.reactive的TS写法

reactive() 也会隐式的依据数据推导类型

默认值属性是固定的,推荐使用类型推导

// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue3 在线医疗' })  

根据默认值推导不出我们需要的类型,推荐使用接口或者类型别名给变量指定类型

// 我们想要的类型:{ title: string, year?: number }
type Book = {
  title: string
  year?: number
}
const book: Book = reactive({ title: 'Vue3 在线医疗' })
book.year = 2022

官方:不推荐使用 reactive() 的泛型参数,因为底层和 ref() 实现不一样。

6.computed和TS

computed() 会从其计算函数的返回值上推导出类型

import { ref, computed } from 'vue'

const count = ref(100);
// 推导出doubleCount类型为number
const doubleCount = computed(() => count.value * 2);

通过泛型参数显式指定类型

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

7.事件处理与TS

不加类型,event默认是any,类型不安全


<template>
  <input type="text" @change="handleChange" />
</template>
<script setup lang="ts">
// 提示:参数“event”隐式具有“any”类型。  
const handleChange = (event) => {
  console.log(event.target.value)
}
</script>

处理类型

// `event` 隐式地标注为 `any` 类型,如何指定:event 类型?
// 1. @change="handleChange($event)"" 查看$event类型
// 2. 鼠标摸一下事件 @change 查看类型
const handleChange = (event: Event) => {
  // `event.target` 是 `EventTarget | null` 类型,如何指定具体类型?
  // document.querySelector('input') 查看返回值类型
  console.log((event.target as HTMLInputElement).value)
}

8.Ref与TS

模板 ref 需要通过一个显式指定的泛型参数,建议默认值 null


<template>
  <input ref="el" />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const el = ref<HTMLInputElement| null>(null)

onMounted(() => {
  el.value?.focus()
})
</script>

注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫
这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null

9.非空断言

处理类型可能是 null 或 undefined 的值

<template>
  <div>App组件</div>
  <input type="text" ref="input" value="abc">
</template>

1.可选链

<script setup lang="ts">
import { onMounted, ref } from 'vue';


const input = ref< HTMLInputElement | null >(null)

onMounted(()=>{
  // 只能访问
  console.log(input.value?.value);
})

</script>


2.逻辑判断

if (input.value) {
    console.log(input.value.value)
    input.value.value = '123'
  }

3.非空断言

一定要确定不为空

// 一定要确定不为空!!!
  console.log(input.value!.value)
  input.value!.value = '123'

TypeScript类型声明文件

基本介绍

TypeScript 类型声明文件(Type Declaration Files)通常使用 .d.ts 扩展名,并用于描述 JavaScript 模块、库或框架的类型信息。

TS的两种文件类型

.ts 文件

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

.d.ts 文件

  1. 只包含类型信息的类型声明文件
  2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
  3. 用途:为 JS 提供类型信息

.ts 是 implementation 代码实现文件
.d.ts 是 declaration 类型声明文件
如果要为 JS 库或者模块提供类型,就需要类型声明文件

(更新待续)

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值