当前位置: 首页 > news >正文

Vue3+TS项目---实用的复杂类型定义总结

namespace

概念

在TypeScript中,namespace是一种用于组织代码得结构,主要用于将相关得功能(例如类、接口、函数等)组合在一起。它可以帮助避免命名冲突,尤其是在大项目中。

用法

1.定义命名空间

使用namespace关键字定义一个命名空间,并在其中包含类、接口、函数等等。以下举例是我在项目中定义得接口参数类型,因为我得接口参数类型是放在一个文件中进行维护得,所以难免会出现相同参数名不同类型得情况出现。所以这里可以使用namaspace来定义不同模块下得接口参数类型。

declare namespace Api {namespace Auth {type systemInfo = {logo: string;systemName: string;nickname: string;avatar: string;email: string;tenantId: string;}type Domain = {domainName: string;}}
}
2.访问命名空间中的成员

需要使用命名空间名来访问其成员,使用.操作符。

/*** 获取系统名称,logo等信息* @param domainName*/
export function fetchPostSystemInfo(params?: Api.Auth.Domain){return request<Api.Auth.systemInfo>({url: 'xxxxx',method: 'get',params})
}

以上举例限制fetchPostSystemInfo异步请求中传去的参数params的类型约束是Api.Auth.Domain,

调用时通过request<Api.Auth.systemInfo>明确指定响应数据类型是Api.Auth.systemInfo,确保调用者可以在代码中获得类型提示和安全的类型检查。

3.使用
const systemInfo:Api.Auth.systemInfo = reactive({logo: '',systemName: '',nickname: '',avatar: '',email: '',
})async function getSystemInfo() {const { data, error } = await fetchPostSystemInfo({ domainName: window.location.hostname });if (!error && data) {Object.assign(systemInfo, data);}
}

类型定义扩展

泛型类型

实例1
declare namespace Api {namespace Auth {type emailToken<T = any> = {email: string;} & T;}
} 

这段代码定义了一个泛型类型emailToken,它由一个对象组成,包含一个名为email的字符串属性,并且可以通过泛型参数T扩展。T默认是any类型。

具体含义是:

  • emailToken<T = any >:这是一个泛型类型定义,其中T是一个可选的泛型参数,默认值是any。如果你在使用时不传入T,则T的类型会是any。
  • { email : string } & T:这表示emailToken 类型是由两个部分组成的对象类型:
    • 一个固定的属性email,类型是string
    • 通过& T,可以扩展其他属性,这些属性来源T。也就是说,emailToken是一个包含email属性的对象,并且可以包含任何你传入的T类型的属性

举例如下:

type CustomToken = emailToken<{token: string }>;
// CustomToken 类型为 { email: string, token: string }

在上面的例子中,CustomToken类型包含两个属性:email和token,其中email是固定的,而token是通过传入T定义的。

使用:

/*** 获取验证码* @param email* return string*/
export function fetchGetEmailToken(params?: Api.Auth.emailToken) {return request<string>({url: '/usercenter/api/uc/otp/request/email',method: 'post',)}
}let emailToken: string = '';
async function getEmailToken(email: string) {const { data: token, error } = await fetchGetEmailToken({ email });if (!error && token) {emailToken = token || '';window.$message?.success?.('验证码发送成功')return emailToken;}
}

实例2
type Response<T = unKnown> = {statusCode: number;message: string;success: boolean;timestamp: string;result: T
}
  • 泛型T:
    • Response是一个泛型类型,接受一个类型参数T,默认值为unknowm。
    • T可以是任何类型,允许Response在使用时具有灵活性
  • 属性
    • statusCode: string; :表示后端服务的请求状态码,例如 401,200
    • message: string;:表示后端服务的响应消息,提供对状态的更详细说明。
    • result: T; :表示响应的具体数据,类型为T。这允许Response用于多种数据结构
示例1 返回简单数据
const simpleResponse:Response<string> = {statusCode: 200,message: "request is success",timestamp: "1729159210638",success: true,result: "Some string data"
}

在这个例子中,result是一个字符串

示例2 返回对象数据
type User = {id:number;name:string;
}
const userResponse:Response<User> = {statusCode: 200,message: "request is success",timestamp: "1729159210638",success: true,result: {id: 1,name: 'Hzz'}
}

在这个例子中,data是一个User类型的对象

示例3 返回数组数据
cosnt usersResponse: Response<User []> = {statusCode: 200,message: "request is success",timestamp: "1729159210638",success: true,result:[{ id: 1, name: "Alice" },{ id: 2, name: "Bob" }]
}

这里,data是一个User对象的数组

扩展用法

可以通过不同的方式扩展Response类型,以适应更复杂的需求。

添加分页功能

假设你希望响应支持分页,可以扩展Response来包括分页信息:

type PaginatedResponse<T> = {items: T[];total: number;
}type Response<T = unknowm> = {statusCode: number;message: string;success: boolean;timestamp: string;result: PaginatedResponse<T>; // 包含分页信息
}
type User = {id:number;name:string;
}
// 使用示例
const paginatedUserResponse:Response<User> = {statusCode: 200,message: "request is success",timestamp: "1729159210638",success: true,result:{items:[{id: 1, name: "Alice"},{id: 1, name: "Alice"}],total: 2}
}

结合错误信息

如果你希望在失败的情况下包含错误信息,可以扩展响应结构:

type ErrorResponse = {errorCode: string;errorDetails?: string;
}type Response<T = unknown> = {statusCode: number;message: string;success: boolean;timestamp: string;result?: T; // result是可选的error?: ErrorResponse;
}const errorResponse: Response = {statusCode: 200,message: "request is success",timestamp: "1729159210638",success: true,error:{errorCode: '404',errorDetails: "The user with the specified ID does not exist.",}
}

总结:

  • Response是一个灵活的泛型类型,用于描述后端服务的响应,包括状态、消息和具体数据
  • 通过将T用于result属性,可以轻松适应不同的数据结构

映射类型

declare module "@elegant-router/types" {export type RouteLayout = "base" | "blank" | "baseVariant";/*** route map*/export type RouteMap = {"root": "root";"403": "/403";"404": "/404";"500": "/500";"application": "/application";"application_develop": "/application/develop";"client": "/client";};/*** route key*/export type RouteKey = keyof RouteMap;/*** route path*/export type RoutePath = RouteMap[RouteKey];/*** custom route key*/ export type CustomRouteKey = Extract<RouteKey,| "root"| "not-found"| "exception"| "exception_403"| "exception_404">;
}

以上定义类型主要用来描述路由映射和键、值得类型,详细解析如下:

RouteMap 类型 是一个对象,它描述了路由得命名和对应关系

RouteKey 类型

  • keyof 操作符用于获取某个类型得键的集合
  • 在这里,keyof RouteMap将会提取RouteMap中所有的键,形成一个联合类型,也 就是 "403" | "404 | "500" | "application" | "application_develop" | "client"

因此,RouteKey 类型将会是:

type RouteKey = "403" | "404" | "500" | "application" | "application_develop" | "client";

在实际开发中常用于确保路由键与路由路径的映射是安全的和类型化的。例如,使用RouteKey来引用某个路由时,TypeScript会确保你只能使用定义过的路由键,避免拼写错误等问题。

RoutePath 类型

在TypeScript中,使用方括号[]可以通过索引访问对象类型的值,语法如下:

T[K]
  • T是一个对象类型
  • K是T的键(可以是单个键或者键的联合类型)

通过T[k]这种索引访问方式,可以获取T中键K对应的值的类型。如果K是联合类型,结果会是所有键对应值的联合类型。

因此,在 exporttypeRoutePath = RouteMap[RouteKey]; 中

  • RouteMap 是一个对象类型 ,它的键是RouteKey,即 "403" | "404" | "500" | "application" | "application_develop" | "client" ,对应的值的路径字符串是对象的值,
  • RouteMap[RouteKey] 会返回 RouteMap 中键的值的类型
RouteMap["root"]    // "/"
RouteMap["403"] // "/403
...

最终结果:

type RoutePath = "/" | "/403" | "/404" | "/500" | "/application" | "/application/develop" | "/client"; 

总结:这种用法可以在需要动态获取某些键对应值的场景中确保类型安全,比如在路由系统中确保路径类型和路由键的一致性。

Exclude 类型工具

Exclude<T, U>是TypeScript内置的条件类型,它用于从类型T中排除那些可以分配给类型U的成员。它的定义是:

type Exclude<T, U> = T extends U ? never : T;
  • 如果T中某个类型能够分配给U(T extends U 为 true),那么返回never类型(表示排除该成员)
  • 否则,返回T本身

分析以下类型结果:

const RouteKey = "403" | "root" | "not-found" | "application" | "application_develop" | "client"; | "/client"; 
type I18nRouteKey = Exclude<RouteKey, 'root' | 'not-found'>;const RouteKey1 = 'root' | 'not-found'
type I18nRouteKey1 = Exclude<RouteKey1, 'root' | 'not-found'>;

I18nRouteKey:

  • RouteKey中排除 'root' | 'not-found' 这两个键
  • 那么 I18nRouteKey的最终结果是:type I18nRouteKey = "403" | "application" | "application_develop" | "client"; | "/client";

I18nRouteKey1:

  • RouteKey1中排除 'root' | 'not-found' 这两个键
  • 那么 I18nRouteKey1的最终结果是 type I18nRouteKey1 = never

结论:

I18nRouteKey1 的结果是never,是因为 因为 RouteKey1 中只有 "root""not-found"

当 RouteKey 中有其他键时, I18nRouteKey 会成为这些剩余键的联合类型。

Omit 工具类型

Omit<T, K> 是 TypeScript的内置工具类型,用于从类型T中排除指定的键K。

  • T:要修改的原始类型
  • K:要排除的键,可以是单个键或者多个键的联合类型

举例说明:

定义Breadcrumb结构
interface Menu {key: string;// 唯一标识符,用于识别菜单项label: string;// 显示给用户的菜单项标签i18nKey?: I18n.I18nKey | null;// 可选的国际化键,用于多语言支持routeKey: RouteKey;// 路由的键,表示该菜单项对应的路由routePath: RoutePath;// 路由的路径,指向该菜单项的具体路径icon?: () => VNode;// 可选的图标,返回一个虚拟节点(通常用于渲染图标)children?: Menu[];// 可选的子菜单项,数组类型,表示该菜单项下的子菜单
}type Breadcrumb = Omit<Menu, 'children'> & {options?: Breadcrumb[];
}

解释:

  • Omit<Menu, 'children'>: 使用Omit工具类型从Menu接口中排除children属性。这样,Breadcrumb不会直接包含children。
  • & { options?: Breadcrumb[]; }:通过交叉类型将Omit的结果与一个新对象类型结合,这个新类型包含一个可选的属性options,其类型为Breadcrumb[],表示该面包屑导航项包含其他Breadcrumb对象的数组
Breadcrumb 的最终结构
type Breadcrumb = {key: string;// 继承自 Menulabel: string;// 继承自 Menui18nKey?: I18n.I18nKey | null;// 继承自 MenurouteKey: RouteKey;// 继承自 MenuroutePath: RoutePath;// 继承自 Menuicon?: () => VNode;// 继承自 Menuoptions?: Breadcrumb[]; // Breadcrumb[];
}
用法示例

假设我们要构建一个面包屑导航,可以使用Breadcrumb类型如下:

const breadcrumn:Breadcrumb = {key: "home",label: "Home",routeKey: "root",routePath: "/",icon: () => <span>🏠</span>, //可选的图标,返回一个虚拟节点options:[{key: "about",label: "About us",routeKey: "about",routePath: "/about"icon: () => <span>ℹ️</span>, // 子面包屑的图标options: [//....]},{key: "contact",label: "Contact",routeKey: "contact",routePath: "/contact",icon: () => <span>📞</span>, // 子面包屑的图标}]
}

说明

  • keylabelrouteKeyroutePath 属性是必需的,定义了面包屑项的基本信息。
  • icon 属性是一个可选的函数,返回一个虚拟节点(这里使用了简单的 Emoji 作为示例图标)。
  • options 属性用于定义子面包屑项,支持嵌套结构。

相关文章:

Vue3+TS项目---实用的复杂类型定义总结

namespace 概念 在TypeScript中&#xff0c;namespace是一种用于组织代码得结构&#xff0c;主要用于将相关得功能&#xff08;例如类、接口、函数等&#xff09;组合在一起。它可以帮助避免命名冲突&#xff0c;尤其是在大项目中。 用法 1.定义命名空间 使用namespace关键…...

尚硅谷rabbitmq2024 工作模式路由篇 第11节 答疑

String exchangeName "test_direct"; /! 创建交换机 人图全 channel.exchangeDeclare(exchangeName,BuiltinExchangeType.DIREcT, b: true, b1: false, b2: false, map: null); /1 创建队列 String queue1Name "test_direct_queue1"; String queue2Name &q…...

HTTP vs WebSocket

本文将对比介绍HTTP 和 WebSocket &#xff01; 相关文章&#xff1a; 1.HTTP 详解 2.WebSocket 详解 一、HTTP&#xff1a;请求/响应的主流协议 HTTP&#xff08;超文本传输协议&#xff09;是用于发送和接收网页数据的标准协议。它最早于1991年由Tim Berners-Lee提出来&…...

R语言医学数据分析实践-数据读写

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 R编程环境的搭建-CSDN博客 在分析公共卫生数据时&#xff0c;数…...

JavaWeb环境下Spring Boot在线考试系统的优化策略

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于JavaWeb技术的在线考试系统设计与实现的开发全过程。通过分析基于Java Web技术的在线考试系统设计与实现管理的不足&#xff0c;创建了一个计算机管理基于Ja…...

ETL技术在金蝶云星空与旺店通WMS集成中的应用

金蝶云星空数据集成到旺店通WMS的技术案例分享 在数字化转型的背景下&#xff0c;现代企业对系统间的数据集成需求日益增加。本篇文章将以“组装入库>其他入库单-1”方案为例&#xff0c;详细解析如何通过轻易云数据集成平台&#xff0c;实现金蝶云星空与旺店通WMS之间的数…...

【力扣热题100】3194. 最小元素和最大元素的最小平均值【Java】

题目&#xff1a;3194.最小元素和最大元素的最小平均值 你有一个初始为空的浮点数数组 averages。另给你一个包含 n 个整数的数组 nums&#xff0c;其中 n 为偶数。 你需要重复以下步骤 n / 2 次&#xff1a; 从 nums 中移除 最小 的元素 minElement 和 最大 的元素 maxElement…...

机器学习拟合过程

import numpy as np import matplotlib.pyplot as plt# 步骤1: 生成模拟数据 np.random.seed(0) X 2 * np.random.rand(100, 1) y 4 3 * X 2 * X**2 np.random.randn(100, 1)# 步骤2: 定义线性模型 (我们从随机权重开始) w np.random.randn(2, 1) b np.random.randn(1)#…...

如何快速部署一套智能化openGauss测试环境

一、openGauss介绍 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行&#xff0c;允许用户自由地复制、使用、修改和分发软件。openGauss内核深度融合了华为在数据库领域多年的研发经验&#xff0c;结合企业级场景需求&#xff0c;持续构建竞争力…...

【设计模式】深入理解Python中的原型设计模式

深入理解Python中的原型设计模式 在软件开发中&#xff0c;有时需要创建对象的过程非常复杂或者代价较高&#xff0c;而在同一类对象的实例之间有很多重复的属性。为了避免重复构造对象&#xff0c;提升性能和效率&#xff0c;原型设计模式&#xff08;Prototype Pattern&…...

Django CORS配置方案

参考 https://pypi.org/project/django-cors-headers/ 在setting.py中设置 INSTALLED_APPS [......corsheaders, #添加此行 ]MIDDLEWARE[......corsheaders.middleware.CorsMiddleware, #添加此行django.middleware.common.CommonMiddleware,#django.middleware.csrf.CsrfVi…...

2024年开放式耳机哪个牌子好?推荐最好的顶级开放式耳机品牌

在当下&#xff0c;开放式耳机逐渐成为众多消费者的新宠。与传统入耳式耳机相比&#xff0c;开放式耳机展现出诸多独特之处。它可以呈现出更清晰的音质效果&#xff0c;让用户有更美妙的听觉体验。在佩戴感上&#xff0c;开放式耳机更为舒适&#xff0c;不会给耳朵带来压迫感。…...

零基础读懂Stable Diffusion!

前言 一文搞懂Stable Diffusion是什么&#xff0c;怎么训练和使用&#xff0c;语义信息影响生成图片的过程。>>[][加入极市CV技术交流群&#xff0c;走在计算机视觉的最前沿] 前几个月AIGC可谓是大热了一把&#xff0c;各种高质量的生成图片层出不穷&#xff0c;而其中…...

Hash Join 和 Index Join工作原理和性能差异

在数据库查询中&#xff0c;Hash Join 和 Index Join 是两种常见的表连接策略。了解它们的工作原理和性能差异有助于设计高效的数据库查询。我们可以使用 Java 模拟这两种不同的连接方式&#xff0c;并进行性能对比。 1. Hash Join 和 Index Join 的概念&#xff1a; Hash Joi…...

Apifox简介及使用

Apifox 是一款集 API文档管理、接口调试、接口自动化测试 和 Mock 功能于一体的全功能工具&#xff0c;旨在为开发者和测试人员提供一个高效的一站式解决方案。它融合了 Postman、Swagger、JMeter 等工具的优势&#xff0c;能够极大地提升团队协作和 API 开发的效率。 在实际开…...

十、IPD 实施细节(产品设计与开发管理)

产品设计与开发管理 产品设计与开发管理是IPD(集成产品开发)实施过程中的核心环节。它确保从概念设计到最终产品的实现能够按照预定的质量、成本、进度目标顺利完成,并与市场需求、技术发展及企业战略保持一致。IPD强调产品设计与开发管理过程中跨职能团队的协作、流程的系…...

MySQL-13.DQL-聚合函数

一.DQL-分组查询 二.聚合函数 -- DQL:分组查询 -- 聚合函数 -- 1.统计该企业员工数量 count select count(id) from tb_emp; select count(job) from tb_emp;select count(A) from tb_emp; select count(*) from tb_emp;-- 2.统计该企业最早入职的员工 min select min(entr…...

为什么跟别人学习如何证明定理要远比使用定理更有意义

目录 背景 为什么跟别人学习 什么是高人&#xff0c;如何判断 高人定义 如何判断一个人的能力&#xff1f; 如何考量一个人的成就&#xff1f; 只知道使用定理的局限性 1. 缺乏灵活性和适应性 2. 无法创新或拓展新方法 3. 容易误用或误解定理 4. 难以推理和分析复杂问…...

Qt在Win,Mac和Linux的开机自启设置

Windows Windows 使用注册表来管理开机自启的应用程序。 void runWithSystem(const QString& name, const QString& path, bool autoRun) {QSetting reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSetting::NativeForma…...

spring boot热部署

使用热部署解决了每次都需要重新启动的问题&#xff0c;但不过热部署的在对于改动比较小时速度可能快一些&#xff0c;改动大的话尽量停止 1.使用热部署之前需要在pom.xml文件中导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifa…...

网关与蓝牙网关有什么不同之处?

尽管蓝牙网关也属于网关的一种&#xff0c;但在实际应用和功能特性上&#xff0c;它们之间存在着显著的差异。接下来&#xff0c;我们将深入探讨蓝牙网关的独特之处&#xff0c;并与传统网关进行对比分析。 一、网关与蓝牙网关的共同之处 一对多配对能力&#xff1a;无论是网关…...

JAVA计算双十一多产品实付款优惠券的省钱方案

为了计算双十一期间多产品使用优惠券后的实付款省钱方案&#xff0c;我们需要一个更复杂的逻辑来处理优惠券的应用和叠加规则。以下是一个简化的Java示例&#xff0c;用于展示如何计算多种优惠券应用于多个产品后的实付款金额&#xff0c;并找出最省钱的方案。 首先&#xff0…...

零售行业的数字化营销转型之路

一方面&#xff0c;市场竞争激烈&#xff0c;电商平台、新兴品牌和跨界对手带来巨大压力。另一方面&#xff0c;消费者需求变化迅速&#xff0c;更加追求个性化、多元化和便捷化的购物体验&#xff0c;同时传统零售企业还面临着高成本压力&#xff0c;如租金、人力和库存等。 然…...

js的for in 和 for of的详解

for...in 和 for...of 是 JavaScript 中的两种循环结构&#xff0c;它们用于不同的场景&#xff0c;适用于不同的数据类型。下面将详细介绍它们的用法、区别以及适用场景。 1. for...in 循环 for...in 用于遍历对象的可枚举属性&#xff08;包括继承的属性&#xff09;。 语…...

前端工具函数库

流行的前端工具函数库 lodashlodash-es&#xff1a;用lodash-es代替lodashes-toolkit&#xff1a;https://www.npmjs.com/package/es-toolkitradash&#xff1a;https://github.com/sodiray/radash 补充信息&#xff1a; antd-mobile 已不再依赖 lodash&#xff0c; 淘汰 lo…...

Java程序设计:Spring boot(4)——Freemarker Thymeleaf视图技术集成

1 Freemarker 视图集成 SpringBoot 内部⽀持 Freemarker 视图技术的集成&#xff0c;并提供了⾃动化配置类 FreeMarkerAuto Configuration&#xff0c;借助⾃动化配置可以很⽅便的集成 Freemarker基础到 SpringBoot 环境中。这⾥借助⼊⻔项⽬引⼊ Freemarker 环境配置。 Start…...

JavaScript 第19章:Web Storage

在JavaScript中&#xff0c;Web存储&#xff08;Web Storage&#xff09;提供了一种在用户浏览器中持久化数据的方式。这里我们会探讨localStorage、sessionStorage以及IndexedDB&#xff0c;并提供一些简单的示例代码来展示它们的用法。 localStorage localStorage允许你在用…...

[山河2024] week2

官方WP出得很快。对照官的写下私的。大概出入不大&#xff0c;毕竟第2周。后边的才难。 Crypto E&R RSA因子分解题&#xff0c;把q的2进制反转后与p异或。关于异或的题很多&#xff0c;这个还真是头一回见&#xff0c;不过爆破方法还是一样的。 r_q int(bin(q)[2:][::…...

无限可能LangChain——开启大模型世界

什么是大语言模型&#xff1f; 大语言模型是一种人工智能模型&#xff0c;通常使用深度学习技术&#xff08;如神经网络&#xff09;来理解和生成人类语言。这些模型拥有非常多的参数&#xff0c;可以达到数十亿甚至更多&#xff0c;使得它们能够处理高度复杂的语言模式。 我…...

URL路径以及Tomcat本身引入的jar包会导致的 SpringMVC项目 404问题、Tomcat调试日志的开启及总结

一、URL路径导致的 SpringMVC项目 404问题 SpringMVC项目的各项代码都没有问题&#xff0c;但是在页面请求时仍然显示404&#xff0c;编译的时候报了下面的问题&#xff1a; org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录…...