TypeScript实战系列之合理运用类型

目录

  • 介绍
    • any 和 unknow
    • nerve 的用途
    • 断言
    • type 和 interface
    • declare 关键字的作用
    • 联合类型 和 类型守卫
    • 交叉类型

介绍

这篇主要介绍下ts 常用的基本类型和一些常用的技巧性技能

any 和 unknow

any 和 unknown 是两个类型关键字,它们用于处理类型不确定或未知的情况。
区别: unknown 只是在接受的传参的时候 忽略检查 当你需要使用的它的属性或者方法的时候 必须给它断言 要不然编译不通过,而any 在接受 和使用 都会去屏蔽 检验。

例子
场景: 需要定义一个函数给外部使用,但是外部的类型无法确定,要保证所有类型都能接受。但函数内部只对某一些类型处理。这时候就可以使用 any 和 unknow。

使用 any时,下面这段代码ts 不会给出编译错误提示,但实际运行会报错 。因为any 类型 在接受和使用的时候会屏蔽检查

function toFixedTwo(params:any){return params.toFixed(2)
}toFixedTwo('100')

使用 unknow时,ts 在函数定义的时候就会给出编译错误提示!

function toFixedTwo(params:unknown){return params.toFixed(2)
}

在这里插入图片描述

结论:unknown 类型要安全得多,因为它迫使我们执行额外的类型检查来对变量执行操作。所以当我们并不知道第三方传过来的字段时,我们用unknow 去接受处理,尽量避免使用any。

最终如下:

function toFixedTwo(params:unknown){if(typeof params === "number"){return params.toFixed(2)}else {throw new Error("TypeError")}
}

nerve 的用途

never 是一个特殊的类型声明,它表示一个永不出现的值。never 单独使用的场景比较少,一般在封装工具类型时用的多(这个后续介绍类型体操中会使用的很频繁,运用了never特性。它可以)

下面列几个 never 的例子

function error(message: string): never { throw new Error(message); 
}function loop(): never { while (true) { // do something } 
}

实际开发中的运用:

假如你针对某一个枚举值有处理逻辑

enum TestNeverEnum {FIRST,SECOND,
}function getEnumValue(value: TestNeverEnum):string { switch (value) { // 这里 value 被收窄为 TestNeverEnum.FIRSTcase TestNeverEnum.FIRST: return "First case"; // 这里 value 被收窄为 TestNeverEnum.SECONDcase TestNeverEnum.SECOND: return "Second case"; // returnValue 是 never 类型default: const returnValue: never = value; return returnValue; } 
}

因为 case已经处理了全部的情况 所以不会走到default 所以 value 会被推断为 never

后面 假设增加的枚举的类型:

enum TestNeverEnum {FIRST,SECOND,THREE
}function getEnumValue(value: TestNeverEnum):string { switch (value) { // 这里 value 被收窄为 TestNeverEnum.FIRSTcase TestNeverEnum.FIRST: return "First case"; // 这里 value 被收窄为 TestNeverEnum.SECONDcase TestNeverEnum.SECOND: return "Second case"; // returnValue 是 never 类型default: const returnValue:never = value; return returnValue; } 
}

在这里插入图片描述
如果你不继续增加case value 的类型就不会是never 就无法被赋值给never 就会编译不过了,可以避免漏写逻辑处理情况的发生。

断言

有时侯 通过内置的api获得的值类型可能 范围很大。但是实际你是知道具体类型的 你就可以使用断言来指定变量的类型。

例子:
场景1:

const myCanvas = document.getElementById("main_canvas")

在这里插入图片描述
返回的是 HTMLElement 类型 但是实际你是知道他是一个cavans 容器

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement

在这里插入图片描述
就会有对应的代码提示了!

场景2:
你需要遍历某个object 类型时,编译会不通过!

let obj = {name:'kd',age:35
}Object.keys(obj).forEach(item=>{console.log(obj[item]);
})

在这里插入图片描述
实际我们知道这段代码是没有问题的,Object.keys() 方法 返回的是一个 string[] 这时候就需要使用断言来告诉编译器它正确的类型。
有两种解决方法:

  1. 是缩小item 的类型范围 将 string 类型 变为 obj 所拥有key 的联合类型
let obj = {name:'kd',age:35
}type ObjKeyType = keyof typeof objObject.keys(obj).forEach(item=>{console.log(obj[item as ObjKeyType]);
})

在这里插入图片描述
另一种写法:

let obj = {name: "kd",age: 35,
};type ObjKeyType = keyof typeof obj;(Object.keys(obj) as Array<ObjKeyType>).forEach((item) => {console.log(obj[item]);
});

2: 去扩大obj 的类型

let obj = {name: "kd",age: 35,
};Object.keys(obj).forEach((item) => {console.log((obj as Record<string,any>)[item]);
});

总结:断言用于在代码中明确表达对某个条件的假设或要求。它允许你对类型进行额外的检查和限制,以确保代码在运行时的正确性。

type 和 interface

type 和 interface 是用于定义类型的两种不同方式。它们之间有以下区别

  1. 定义类型范围不同
    interface 只能定义对象类型或接口当名字的函数类型
    type 可以定义任何类型,包括基础类型 联合类型 交叉类型
interface PeopleType {age:numbername:string
}interface Run {(name:string):void
}type People = {age:numbername:string
}type RunType = (name:string)=>voidtype Num = numbertype BaseType = string |number|Symboltype AllType = {name:string}&{age:number}
  1. interface 可以继承 一个或者多个接口和类。type 无法继承,但是能使用交叉类型来实现部分继承
interface Animal {name:string
}interface Size {long:number
}interface Cat extends Animal,Size {jump:(arg:string)=>void
}let cat :Cat = {name:'cat',long:100,jump(arg) {console.log(arg);},
}
  1. interface 可以合并声明 type 不行
interface People {name:string,address:string
}interface People {age:number
}let people: People = {name:'kd',address:'usa',age:35
}
  1. type还提供了很多的关键字使用,这是interface不具备的 ,比如in extends infer 关键字 (后续再介绍使用)

总结: 选择使用 type 还是 interface 取决于你的需求。如果你需要定义具体的类型并且不希望它被扩展,可以使用 type;如果你需要定义可扩展的接口或具有默认属性的类型,可以使用 interface;

declare 关键字的作用

declare 关键字的重要特点是,它只是通知编译器某个变量是存在的,不用给出具体实现。比如,只描述函数的类型,不给出函数的实现,如果不使用declare,这是做不到的。

场景1: 自己的ts项目使用外部库定义的函数,编译器会因为不知道外部函数的类型定义而报错,这时就可以在自己的脚本里面使用declare关键字,告诉编译器外部函数的类型。
一般来说第三方库都会有对应的d.ts 声明文件,而声明文件中就大量的使用了 declare 来声明类型。

例子:我从外部引入了 一个 js 作用就是 在window 对象上挂载 sayHello 方法,在代码中可以直接使用,但是ts 并不知道 会报错。这时候 declare 关键字就派上用场了。

window.sayHello = function (arg) {console.log('hello---'), arg;
}
sayHello('hello')

在这里插入图片描述
这时候在全局 .d.ts 文件中 使用declare 关键字 声明下

// vite-env.d.ts
declare function sayHello(arg: string): void;

这段代码 就相当于告诉ts 我全局有一个 sayHello 的方法, 编译就通过了 。

场景2: 使用declare关键字声明资源

例如,如果我们想在代码中使用 PNG(直接使用ts 会报找不到模块,因为ts 只认识),我们可以创建如下声明:

declare module '*.png' {const src: string;export default src;
}

场景3: 使用声明关键字进行全局增强

注意:在使用 global 全局类型时, ts 不会将d.ts 文件当做一个模块来看,而是一个全局文件,外部文件无法使用 global 下的类型。

//global.d.ts
declare global { type Type1 = {...}
}declare type Type2 = {}//demo.ts
let a:Type1  //error 类型Type1不存在
let b:Type2  //success 因为这个文件中没有任何"import"它是一个全局文件

需要你使用 export 或者 import 关键字 来表明这是一个模块

export {}; 
declare global { type Type1 = {...}
}declare type Type2 = {}

这时候 Type1 可以使用, 但是这时候 Type2 了。因为 这时候 d.ts 文件已经是模块化了,外部无法自己使用 Type2 ,因为 Type1 挂在global 下 所以可以获取。

正常一般推荐 使用 全局类型 全部写到 global下

export {}; 
declare global { type Type1 = {...}type Type2 = {}
}

联合类型 和 类型守卫

在 TypeScript 中,联合类型(Union Types)表示取值可以为多种类型中的一种。使用|分隔每个类型,其格式为:Type1|Type2|Type3。
例如:

type sortStatus = 0|1|2
type codeType = '200'|'400'|'500'

针对对象时使用联合类型时:
使用联合类型的变量 可以接收
1.联合类型中任意一种数据类型全部属性和方法
2.也可以是全量满足其中一种类型,再加上另一个类型的部分属性和方法

type Object1 = {name:string,age:number,address:string,height:number
}type Object2 = {name:string,age:number,run:(arg:string)=>void
}// 任意一种
let obj1 :Object1|Object2 = {name:'kd',age:35,address:'usa',height:208
}let obj2 :Object1|Object2 = {name:'kd',age:35,run(arg) {console.log(arg);},
}// 全量满足其中一种类型,再加上另一个类型的部分属性和方法
let obj3 :Object1|Object2 = {name:'kd',age:35,address:'usa',run(arg) {console.log(arg);},
}

我们可以同时接收 number 和 string 类型,但是再使用的时候,ts 并不知道变量类型是 string 还是 number 。

type Type1 = string|numberconst one:Type1 = 'str'
const two:Type1 = 123
function handle(arg:Type1){arg.length;arg.toFixed(2)
}

在这里插入图片描述
校验过不了,这时候就需要使用类型守卫了。

先介绍下什么是类型守卫?
在 TypeScript 中,类型守卫可以用于在运行时检查变量的类型,
并在代码块内部将变量的类型范围缩小到更具体的类型。
类型守卫通常使用类型断言、类型谓词、typeof 操作符、instanceof 操作符或自定义的谓词函数来判断变量的具体类型,并根据判断结果收窄变量的类型范围。typeof 类型守卫允许使用 typeof 操作符来在代码中根据变量的类型范围进行条件判断。

改造后:

const isString = (arg:any):arg is string=>{return typeof arg === 'string'
}function handle(arg:Type1){if(isString(arg)){arg.length;} else {arg.toFixed(2)}
}

或者 直接使用typeof

function handle(arg:Type1){if(typeof arg === 'string'){arg.length;} else {arg.toFixed(2)}
}

交叉类型

在 TypeScript 中交叉类型是将多个类型合并为一个类型。通过 & 运算符可以将现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

同名基础类型属性的合并

interface X {c: string;d: string;}interface Y {c: number;e: string}type DD = X & Ylet obj:DD = {d:'22',e:'1',c:''}

在这里插入图片描述
c不能同时为 string 和 number 所以就为 never

同名非基础类型属性的合并:

interface D { d: boolean; }
interface E { e: string; }
interface F { f: number; }interface A { x: D; }
interface B { x: E; }
interface C { x: F; }type ABC = A & B & C;let abc: ABC = {x: {d: true,e: 'semlinker',f: 666}
};

在混入多个类型时,若存在相同的成员,且成员类型为非基本数据类型,那么是可以成功合并。

使用场景:
多个类型都有重复相同的字段时, 使用type定义 利用 交叉类型 来实现继承的效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/441240.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

yolov8数据标注、模型训练到模型部署全过程

文章目录 一、数据标注&#xff08;x-anylabeling&#xff09;1. 安装方式1.1 直接通过Releases安装1.2 clone源码后采用终端运行 2. 如何使用 二、模型训练三、模型部署3.1 onnx转engine3.2 c调用engine模型3.2.1 main_tensorRT.cpp3.2.2 segmentationModel.cpp 一、数据标注&…

爱可声助听器参与南湖区价值百万公益助残捐赠活动成功举行

“声音大小合适吗&#xff1f;能听清楚吗&#xff1f;”今天下午&#xff0c;一场助残捐赠活动在南湖区凤桥镇悄然举行&#xff0c;杭州爱听科技有限公司带着验配团队和听力检测设备来到活动现场&#xff0c;为南湖区听障残疾人和老人适配助听器。 家住余新镇的75岁的周奶奶身体…

1.迭代与递归 - JS

迭代与递归是函数进阶的第一个门槛。迭代就是对已知变量反复赋值变换&#xff1b;递归就是函数体内调用自身。 迭代 一个迭代是就是一个循环&#xff0c;根据迭代式对变量反复赋值。 求近似根&#xff08;切线法&#xff09;&#xff1b; 迭代描述&#xff1a; x 0 x_0 x0…

C语言KR圣经笔记 6.6 表查询 6.7 typedef

6.6 表查询 为了说明结构体的更多方面&#xff0c;本节我们来写一个表查询功能包的内部代码。在宏处理器或编译器的符号表管理例程中&#xff0c;这个代码是很典型的。例如&#xff0c;考虑 #define 语句&#xff0c;当遇到如下行 #define IN 1 时&#xff0c;名称 IN 与其对…

微信小程序如何实现点击上传图片功能

如下所示,实际需求中常常存在需要点击上传图片的功能,上传前显示边框表面图片显示大小,上传后将图形缩放到边框大小。 实现如下: .wxml <view class="{{img_src==?blank-area:}}" style="width:100%;height:40%;display:flex;align-items: center;jus…

spring框架(一)

1、Spring框架&#xff1a;IoC和AOP 服务端三层开发&#xff1a;表现层、业务层、持久层 ssm, springboot, springcloud(微服务&#xff0c;治理组件) Spring框架是一个流行的Java应用程序框架&#xff0c;它提供了许多功能来简化企业级应用程序的开发。其中&#xff0c;控制反…

Selenium 隐藏浏览器指纹特征的几种方式

我们使用 Selenium 对网页进行爬虫时&#xff0c;如果不做任何处理直接进行爬取&#xff0c;会导致很多特征是暴露的 对一些做了反爬的网站&#xff0c;做了特征检测&#xff0c;用来阻止一些恶意爬虫 本篇文章将介绍几种常用的隐藏浏览器指纹特征的方式 1. 直接爬取 目标对…

面试题 02.07. 链表相交(力扣LeetCode)

文章目录 面试题 02.07. 链表相交题目描述解题思路c代码优化后c代码 面试题 02.07. 链表相交 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 …

shell

目录 一.运行方式 二.编程习惯 三.变量 3.1变量的命名 3.3普通变量(局部变量) 3.4特殊变量 3.5变量子串 3.6变量赋值 四.运算方式 4.1$(( )) 4.2let 4.3expr 4.4bc(小数运算) 4.5$[ ] 4.6awk 4.7总结运算方式 五.条件测试语句 5.1文件 5.2条件测试表达式…

IDEA:git 回滚本地提交-git 选择 Reset Current Branch to

前言 回滚提交到本地但是还没有 Push 上去的提交 选择我们要回滚的节点&#xff0c;然后点击 git 选择 Reset Current Branch to… 再选择 Hard 。当我们点击 Reset 的时候&#xff0c;代码就会回滚到单前选中的这个版本

数字时代的工作利器

当谈到使用工作软件的多样选择时&#xff0c;就像是探索灯塔下的海洋般令人兴奋。无论是新进入办公领域的小白&#xff0c;还是经验丰富的职场老将&#xff0c;我们都渴望找到那些能在工作中为我们点燃生产力和创造力的魔法工具。下面是五款备受欢迎且富有创造力的工作软件推荐…

专业120+总分400+宁波大学912信号与系统考研经验电子信息通信集成电路光电

今年考研顺利上岸&#xff0c;专业课912信号与系统120&#xff0c;总分400&#xff0c;被宁波大学录取&#xff0c;回望这一年的复习有过迷茫和犹豫&#xff0c;也有过坚持和坚强&#xff0c;总结一下自己的复习得失&#xff0c;希望对大家复习有所帮助。专业课&#xff1a; 前…

【重磅发布】已开放!模型师入驻、转格式再升级、3D展示框架全新玩法…

1月23日&#xff0c;老子云正式发布全新版本。此次新版本包含多板块功能上线和升级&#xff0c;为用户带来了含模型师入驻、三维格式在线转换升级、模型免费增值权益开放、全新3D展示框架等一系列精彩内容&#xff01; 1月23日&#xff0c;老子云正式发布全新版本。此次新版本…

【vue】图片加载骨架

一、前言 在网速较低或者网站的服务器宽带只有几MB的情况下&#xff0c;网页中的图片加载时&#xff0c;要么空白&#xff0c;要么像打印机一样一行一行地“扫描”出来&#xff0c;为了提升用户体验&#xff0c;可以给图片标签外加一层骨架。 无骨架 有骨架 二、详细设计 每张…

现在我有三个代码块,分别都调用了同一个接口使用相同的数据,请问怎么精简代码,让他只调用一次接口,将数据存储起来让其他函数共同使用.

问题描述: 现在我有三个代码块: 一: const getData async () > {console.log(触发了getData接口)let resultData await getActivityInfo(activityId);console.log(resultData,resultData)let id resultData.id;let shareImg resultData.shareImglet shareSubtitle res…

JWT(JSON Web Token)详解以及在go-zero中配置的方法

目的 对用户进行身份认证和信息交换 RFC 7519 传统方式 通过session保存对话信息&#xff0c;服务端返回一个session id&#xff0c;用户保存这个id在cookie内&#xff0c;然后每次请求都传给服务端 局限性 对于服务器集群难以向每个服务器共享同一session jwt的方式是…

备战蓝桥杯---数据结构与STL应用(基础实战篇1)

话不多说&#xff0c;直接上题&#xff1a; 当然我们可以用队列&#xff0c;但是其插入复杂度为N,总的复杂度为n^2,肯定会超时&#xff0c;于是我们可以用链表来写&#xff0c;同时把其存在数组中&#xff0c;这样节点的访问复杂度也为o(1).下面是AC代码&#xff1a; 下面我们来…

学习MySQL仅此一篇就够了(视图)

视图 介绍及基本语法 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视 图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xf…

我在代码随想录|写代码Day20之二叉树-700. 二叉搜索树中的搜索,98. 验证二叉搜索树,530.二叉搜索树的最小绝对差

学习目标&#xff1a; 博主介绍: 27dCnc 专题 : 数据结构帮助小白快速入门 &#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; ☆*: .&#xff61;. o(≧▽≦)…

TypeScript(八) number和string

1. TypeScript number 1.1. 描述 Number对象是原始数值的包装对象。 1.2.语法 var num new Number(value);;注意&#xff1a;如果一个参数值不能转换为一个数字&#xff0c;将返回NaN&#xff08;非数字值&#xff09;。 1.3. 对象属性 属性描述MAX_VALUE可表示的最大的数…