阿卡不拉阿卡不拉
Vue3
阿卡的博客
Vue3
阿卡的博客
  • CSS

    • ✨flex 布局 👌
  • Vue3

    • 快速入门

      • 搭建工程 👌
      • 模板语法
      • 响应式基础
      • 响应式常用 API
      • 计算属性
      • 类与样式绑定
      • 条件和列表渲染
      • 事件处理
      • 表单处理
      • 生命周期
      • 侦听器
      • 组件介绍
      • Props
      • 自定义事件
      • 组件v-model
      • 插槽
      • 前端路由介绍
      • KeepAlive内置组件
      • 状态管理库
      • 组件库介绍
    • 深入本质

      • 虚拟DOM本质
      • 模板的本质
      • 组件树和虚拟DOM树
      • 数据拦截的本质
      • 响应式数据的本质
      • 响应式的本质
      • 响应式和组件渲染
      • 实现响应式系统 1
      • 实现响应式系统 2
      • 图解EFFECT
      • 实现响应式系统 3
      • 手写computed
      • 手写watch
      • 指令的本质
      • 插槽的本质
      • v-model的本质
      • setup 语法标签
      • 组件生命周期
      • keepalive 生命周期
      • keepalive的本质
      • key的本质
    • 细节补充

      • 【Vue】属性透传
      • 【Vue】依赖注入
      • 【Vue】组合式函数 👌
      • 【Vue】自定义指令
      • 【Vue】插件
      • 【Vue】Transition
      • 【Vue】TransitionGroup
      • 【Vue】Teleport
      • 【Vue】异步组件
      • 【Vue】Suspense
      • 【Router】路由模式
      • 【Router】路由零碎知识
      • 【Router】路由匹配语法
      • 【Router】路由组件传参
      • 【Router】内置组件和函数
      • 【Router】导航守卫
      • 【Router】过渡特效
      • 【Router】滚动行为
      • 【Router】动态路由
      • 【State】通信方式总结
      • 【State】Pinia 自定义插件
      • 【场景】封装树形组件
      • 【场景】自定义 ref 实现防抖
      • 【场景】懒加载
      • 【场景】虚拟列表
      • 【场景】虚拟列表优化
      • 【第三方库】VueUse
      • 【第三方库】vuedragable
      • 【第三方库】vue-drag-resize
      • 【第三方库】vue-chartjs
      • 【第三方库】vuelidate
      • 【第三方库】vue3-lazyload
      • 【场景】Websocket 聊天室
      • 【Vite】✨ 认识 Vite👌
      • 【Vite】配置文件 👌
      • 【Vite】✨ 依赖预构建 👌
      • 【Vite】构建生产版本 👌
      • 【Vite】环境变量与模式
      • 【Vite】CLI
      • 【Vite】Vite 插件
  • 笔面试

    • HTML

      • HTML 面试题汇总
      • 文档声明
      • 语义化
      • W3C 标准组织
      • SEO
      • iframe
      • 微格式
      • 替换元素
      • 页面可见性
    • CSS

      • CSS 面试题汇总
      • CSS 单位总结
      • 居中方式总结
      • 隐藏元素方式总结
      • 浮动
      • 定位总结
      • BFC
      • CSS 属性计算过程
      • CSS 层叠继承规则总结
      • @import 指令
      • CSS3 calc 函数
      • CSS3 媒体查询
      • 过渡和动画事件
      • 渐进增强和优雅降级
      • CSS3 变形
      • 渐进式渲染
      • CSS 渲染性能优化
      • 层叠上下文
      • CSS3 遮罩
    • JavaScript

      • JavaScript 面试题汇总
      • ✨ let、var、const 的区别
      • JS中的数据类型
      • 包装类型
      • 数据类型的转换
      • 运算符
      • ✨ 原型链
      • ✨ this 指向
      • ✨ 垃圾回收与内存泄漏
      • ✨ 执行栈和执行上下文
      • ✨ 作用域和作用域链
      • ✨ 闭包
      • DOM 事件的注册和移除
      • DOM 事件的传播机制
      • 阻止事件默认行为
      • 递归
      • ✨ 属性描述符
      • class 和构造函数区别
      • 浮点数精度问题
      • 严格模式
      • ✨ 函数防抖和节流
      • ✨ WeakSet 和 WeakMap
      • ✨ 深浅拷贝
      • 函数柯里化
      • Node 事件循环
      • 尺寸和位置
      • ✨ 事件循环 👌
    • Promise

      • ✨Promise 面试题考点 👌
      • ✨Promise 基础 👌
      • ✨Promise 的链式调用 👌
      • ✨Promise 的静态方法 👌
      • ✨async 和 await👌
    • 浏览器

      • 浏览器面试题汇总
      • ✨ 浏览器的渲染流程
      • ✨ 资源提示关键词
      • 浏览器的组成部分
      • IndexedDB
      • ✨ File API
      • ✨ 浏览器缓存
      • ✨ 浏览器跨标签页通信
      • Web Worker
    • 网络

      • 网络面试题汇总
      • 五层网络模型 👌
      • 常见请求方法 👌
      • ✨cookie👌
      • 面试题
      • 加密
      • ✨JWT👌
      • ✨ 同源策略及跨域问题 👌
      • 文件上传
      • ✨ 输入 url 地址之后
      • 文件下载
      • ✨ session
      • ✨ TCP
      • ✨ CSRF 攻击
      • ✨XSS 攻击 👌
      • ✨ 网络性能优化
      • 断点续传
      • 域名和 DNS
      • ✨SSL、TLS、HTTPS 的关系
      • ✨ HTTP 各版本差异 👌
      • HTTP 缓存协议
      • ✨ WebSocket
    • 工程化

      • CMJ 和 ESM
      • npx
      • ESLint
    • Vue2

      • Vue 面试题汇总相关
      • 组件通信方式总结
      • 虚拟 DOM
      • v-model
      • 数据响应式原理
      • diff
      • 生命周期详解
      • computed
      • 过滤器
      • 作用域插槽
      • 过度和动画
      • 优化
      • keep-alive
      • 长列表优化
      • 其他 API
      • 模式和环境变量
      • 更多配置
      • 更多命令
      • 嵌套路由
      • 路由切换动画
    • Vue3

      • ✨ Vue3 整体变化 👌
      • ✨ Vue3 响应式变化 👌
      • ✨ nextTick 实现原理 👌
      • 两道代码题 👌
      • Vue 运行机制
      • 渲染器核心功能
      • 事件绑定与更新
    • Cypress

      • Cypress 测试框架面试题
    • 项目

      • FOFA 实习项目

        • /interview/project/fofa/FOFA%E5%AE%9E%E4%B9%A0%E9%A1%B9%E7%9B%AE%E9%9D%A2%E8%AF%95%E7%82%B9.html
      • 低代码问卷系统

        • 低代码问卷项目面试点
      • VR 全景看房

        • VR 全景看房项目面试点
  • TS

    • 快速入门

      • Playground 👌
      • 安装与运行 👌
      • 开发相关配置 👌
      • TS 常见类型 👌
      • 类型声明 👌
  • 工具库

    • 常用第三方工具库
    • JQuery
    • Lodash
    • Animate.css
    • Axios
    • MockJS
    • Moment
    • ECharts
  • 其他知识点

    • ✨ 前端项目打包流程与编译概念详解
    • ✨ 懒加载 👌
    • ✨ 前端路由的核心原理 👌

TS 常见类型 👌

JS 数据类型

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol
  • bigint
  • object

any 类型

any 类型可以绕过类型检查,因此,any 类型的数据可以赋值给任意类型,当然如果没有约束,也没有类型推断,那这个类型就是 any 类型

字面量类型

用于使用 const 声明的变量不能修改,因此其声明的变量对应的类型就是变量的值也叫做字面量类型。

const b = "hello";
const c = null;

let d: "hello";
d = "hello";
// d = "world"; // 报错,只能赋值为hello

联合类型

let v1: string | number | undefined = undefined;
v1 = "hello";
v1 = 123;

let v2: "男" | "女";
v2 = "女";

let v3: "UP" | "DOWN" | "LEFT" | "RIGHT";
v3 = "RIGHT";

数组

数组可以通过类型[]来表示,比如 number[]、string[]、boolean[]等

数组也可以通过Array<elemType>来表示,比如Array<number>、Array<string>、Array<boolean>等

const arr1 = [1, 2, 3, 4, 5];
const arr2: string[] = ["a", "b", "c", "d", "e"];
const arr3: number[] = [1, 2, 3, 4, 5];
const arr4: Array<number> = [1, 2, 3, 4, 5];

空数组默认推断为any[],不过这个和相关类型检查机制有关联, noImplicitAny 为 true 时才会生效。当 noImplicitAny 为 false 时,空数组的类型推断为 never[]。

const arr5 = [];
arr5.push(123);
arr5.push("123");

数组也能联合类型

let arr6: (string | number)[] = [1, "2", 3, "4"];
let arr7: Array<string | number> = [1, "2", "3", 4];
// 注意和下面写法的区别
let arr8: string[] | number[] = [1, 2, 3, 4];
let arr9: Array<string> | Array<number> = ["1", "2", "3", "4"];

元祖类型(Tuple)

一个固定长度的数组,并且数组中每一项的类型确定

const tuple1: [number, number] = [1, 2];
const tuple2: [number, string] = [1, "2"];

场景: 在地图中,使用经纬度坐标来标记位置信息

可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型

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

使用 number[] 的缺点: 不严谨,因为该类型的数组中可以出现任意多个数字

更好的方式: 元组(Tuple)

元组类型时另一种类型的数组,他确切的知道包含多少个元素,以及特定索引对应的类型

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

解释:

元组类型可以确切的标记处有多少个元素,以及每个元素的类型。示例中,元素有两个元素,每个元素的类型都是 number

容易混淆的赋值

let tuple3: [] = []; // 空元组

// tuple3 = [1]; //报错

let value = []; // 这样才表示暂时的空数组,不过类型是any[]

函数

和 js 的区别无非也就是多了参数和返回值类型的类型定义

返回值类型可以进行推断,其实参数的类型也能够通过类型检查的"noImplicitAny": false,配置设定取消

function add(a: number, b: number): number {
  return a + b;
}

const r = add(1, 2);

可选参数与默认参数

function sum1(a: number, b: number, c?: number) {
  console.log(a, b, c);
}
sum1(3, 4); // 3 4 undefined

// 默认参数本身就是可选参数
function sum2(a: number, b: number, c = 10) {
  console.log(a, b, c);
}
sum2(3, 4); // 3 4 10

剩余参数

const fn = (a: number, b: number, ...args: number[]) => {
  console.log(a, b, args[0]);
};

void

函数没有显式的返回值类型,会被默认的推导为void

function print(): void {
  console.log("1.登录");
  console.log("2.注册");
}

泛型

TypeScript 中的泛型是一种工具,它允许在定义函数、接口或类时提供一个类型变量。这种类型变量可以被视为一种特殊的标记,它允许你在不同的地方使用不同的、具体的类型。泛型提供了一种方式来创建可重用的组件,这些组件可以支持多种类型的数据,同时保持类型的安全性。

// 泛型函数
function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<string>("myString"); // 明确指定T为string
// 也可以使用类型推断
let output2 = identity("myString"); // 类型推断,T被推断为字面量myString
let output3 = identity(123); // 类型推断,T被推断为字面量123
console.log(output1, output2, output3);
function getTuple<T>(a: T, b: T) {
  return [a, b];
}
const as = getTuple<string>("hello", "world");

function getTuple<T, U>(a: T, b: U): [T, U] {
  return [a, b];
}
const as = getTuple<string, number>("hello", 123);
function myNumberFilter(
  arr: number[],
  callback: (item: number, index?: number) => boolean
): number[] {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (callback(item)) {
      result.push(item);
    }
  }
  return result;
}
const filterArr1 = myNumberFilter([1, 2, 3, 4, 5], (item) => item % 2 === 0);
console.log(filterArr1);

// 使用泛型
function myFilter<T>(
  arr: T[],
  callback: (item: T, index?: number) => boolean
): T[] {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (callback(item)) {
      result.push(item);
    }
  }
  return result;
}

const filterArr2 = myFilter(["xxx.js", "aaa.java", "bbb.md"], (item) =>
  item.endsWith(".js")
);
console.log(filterArr2);

对象字面量类型

const obj1 = {
  name: "lily",
  age: 18,
};

// 其实是下面的简写:
const obj2: {
  name: string;
  age: number;
} = {
  name: "lily",
  age: 18,
};

// 同样,在函数中也可以使用对象字面量
function getInfo(user: {
  name: string;
  age: number;
}): { name: string; age: number }[] {
  // todos...
  return [
    {
      name: "lily",
      age: 18,
    },
    {
      name: "lucy",
      age: 20,
    },
  ];
}

自定义类型:类型别名与接口

在 TS 中,**类型别名(Type Aliases)和接口(Interfaces)**是两种定义对象类型的方式。它们在很多情况下可以互换使用,但各自有其特点和最佳应用场景。

类型别名是一种为类型创建新名称的方式,就是取一个新的名字。类型别名可以是任何有效的类型,包括基本类型、联合类型、元组等

// 类型别名定义方式
// type TypeName = /* some type */

type Point = {
  x: number;
  y: number;
};

type ID = string | number;

type Age = number;

type User = {
  name: string;
  age: Age;
};

const obj: User = {
  name: "lily",
  age: 18,
};

接口是面向对象的概念,因此它是定义对象结构的一种方式,它描述了对象的形状,即对象应该有哪些属性以及属性的类型。接口主要用于声明对象的结构

// 接口定义方式
interface InterfaceName {
  // structure
}

interface Person {
  id: number;
  name: string;
  age: number;
}

const obj: Person = {
  id: 1,
  name: "lily",
  age: 18,
};

有了自定义的类型之后,可以很方便的在函数和数组中使用

function fn1(user: User) {
  console.log(user.name);
}

const users: User[] = [
  {
    name: "lily",
    age: 18,
  },
  {
    name: "lucy",
    age: 20,
  },
];

类型中的函数声明与可选属性

type InfoFn = (id: number, name?: string) => string;

interface Book {
  id: number;
  name: string;
  price?: number;
  // 两种表示函数的方式
  show(id: number): void;
  filter: (id: number) => void;
  info: InfoFn;
  author: User;
}

const book: Book = {
  id: 1,
  name: "javascript",
  show(id: number) {
    console.log(id);
  },
  filter(id: number) {
    console.log(id);
  },
  info(id: number, name?: string) {
    return "hello";
  },
  author: {
    name: "lily",
    age: 18,
  },
};

交叉类型

type A = {
  id: number;
  name: string;
};
type B = {
  age: number;
};

// 定义交叉类型
type C = A & B;
// 这是联合类型
type D = A | B;

// 注意类型C与类型D的区别
/*
const v5: C = {
  id: 1,
  name: "lily",
  // age: 18, //error 缺少age属性
}
*/

// v6的取值可以是A或B也可以是A+B
const v6: D = {
  id: 1,
  name: "lily",
  age: 18,
};

类型断言

类型断言是一种告诉编译器“我知道我在做什么”的方式。允许你将一个变量指定为更具体或更宽松的类型。

简单来说,TS 根据它的类型推测,并不能确定到底是什么类型。但是我们明确知道一个值的类型,那我们就人为的干涉一下。告诉 TS,这就是某种类型,别怕

语法:

  • 值 as 类型
  • <类型>值
let someValue: any = "this is a string";
let strLength1: number = (<string>someValue).length;
// 如果要写断言,建议用as,因为上面的形式在react中会有歧义。尖括号语法与JSX的标签语法相冲突
let strLength2: number = (someValue as string).length;

非空断言

当你确信某个值不是null或undefined时,可以使用非空断言

语法: 值!,比如someValue!

let maybeString: string | null = "hello";
let definitelyString = maybeString!;
function getRandom(length?: number) {
  if (!length) {
    return undefined;
  }

  return Math.random().toString(36).slice(-length);
}
let s = getRandom(6);
// 可以使用类型断言
(s as string).charAt(0);
// 由于就是字符串和非空的处理,可以使用非空断言
s!.charAt(0);
type Box = {
  id: number;
  name: string;
};

function getBox(): Box | undefined {
  if (Math.random() > 0.5) {
    return {
      id: 1,
      name: "box1",
    };
  }
  return undefined;
}

function createProduction(box: Box) {
  // todos...
}

createProduction(getBox() as Box);
// 非空断言
createProduction(getBox()!);

比如常见的 dom 操作

const inputDom = document.querySelector("input");
inputDom!.addEventListener("change", (e) => {
  console.log((e.target as HTMLInputElement).value);
});

所有的 DOM 相关的类型声明都在核心库定义文件lib.dom.d.ts中,要查找相关的 Element,可以查看 interface HTMLElementTagNameMap

可选链操作符

注意,可选链操作符是 ES2020 新的语法特性(node 14 以上),并不是 TS 的新特性

可选链操作符 ?. 使得我们在尝试访问一个对象的属性或调用一个方法时,如果该对象是 undefined 或 null,不会引发错误,而是会返回 undefined。这样可以避免使用冗长的条件语句来检查对象的每个层级。

interface Address {
  street?: string;
  city?: string;
}

interface Student {
  name: string;
  address?: Address;
}

let student: Student = {
  name: "Rose",
  address: {
    city: "上海",
    // 注意:这里没有提供street属性
  },
};

// 使用可选链安全地访问street属性
let street = student.address?.street; // street将为undefined,但不会抛出错误

console.log(street); // 输出 undefined
最近更新:: 2025/7/23 03:47
Contributors: AK
Prev
开发相关配置 👌
Next
类型声明 👌