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.mzph.cn/news/654127.shtml

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

相关文章

【Sql Server】新手一分钟看懂在已有表基础上修改字段默认值和数据类型

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

力扣日记1.28-【回溯算法篇】93. 复原 IP 地址

力扣日记&#xff1a;【回溯算法篇】93. 复原 IP 地址 日期&#xff1a;2023.1.28 参考&#xff1a;代码随想录、力扣 93. 复原 IP 地址 题目描述 难度&#xff1a;中等 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&…

IP地址定位技术的巧妙运用:企业网络安全的坚实防线

在当今数字时代&#xff0c;企业网络安全成为了至关重要的议题。面对不断增长的网络威胁&#xff0c;企业不得不采用创新性技术来保护其机密信息和关键系统。IP地址定位技术作为一种强大的工具&#xff0c;为企业提供了一种新颖而高效的网络安全保护手段。 IP地址定位技术简介 …

前端Vue v-for 的使用

目录 ​编辑 简介 使用方式 基本使用 v-for"(item, index)中item和index作用 示例 迭代对象 示例 结果 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入…

证券公司怎么选择?福州开股票账户佣金最低是多少?怎么开低佣金账户?

股票交易佣金是指投资者在进行股票交易时&#xff0c;需要向券商支付的手续费。具体的佣金费用根据券商的政策而有所不同&#xff0c;一般分为固定佣金和按比例佣金两种方式。 固定佣金是指交易每一笔固定收取一定金额的佣金&#xff0c;通常适用于较小交易量的投资者&#xf…

github ssh ssh-keygen

生成和使用 SSH 密钥对是一种安全的身份验证方式&#xff0c;用于在你的本地系统和 GitHub 之间进行身份验证。以下是在 GitHub 上生成和使用 SSH 密钥对的基本步骤&#xff1a; 1. 生成 SSH 密钥对 在命令行中执行以下命令来生成 SSH 密钥对&#xff1a; ssh-keygen -C &q…

ATT汇编

指令后缀 AT&T格式的汇编指令有不同的后缀 其中 b表示byte&#xff0c;字节 w表示word&#xff0c;字/两字节 l表示long&#xff0c;32位系统下的long是4字节 q表示quad&#xff0c;意味四重&#xff0c;表示4个字/8字节 寄存器用途 参见 AT&T的汇编世界 - Gemfield…

把数组中的key都取出来然后去重

今日接到一个小需求&#xff0c;一张表有类似这样的数据&#xff1a;&#xff08;下面是一行&#xff09; 但是每行的数据&#xff0c;主要是key不一样&#xff0c;我们想把所有的key取出来&#xff0c;并且做个去重。 首先我先在mysql中&#xff0c; SELECT GROUP_CONCAT(RE…

蓝桥杯---九数组分数

1,2,3 ... 9 这九个数字组成一个分数,其值恰好为1/3,如何组法? 下面的程序实现了该功能,请填写划线部分缺失的代码。 注意,只能填写缺少的部分,不要重复抄写已有代码。不要填写任何多余的文字。 代码 public class _05九数组分数 {public static void test(int[] x){int a …

Linux, Certbot快速申请免费https证书

linux环境. 更新apt,为了能正确的下载certbot apt update 安装certbot apt install certbot 如果之前nginx已经开启着了,先关掉,防止端口占用 nginx -s stop 运行certbot开始获取证书文件 certbot certonly 输入1直接回车,意思就是让certbot模拟一个web服务器执行下面的…

探秘Photoshop | 一站式了解所有相关信息

Photoshop是迄今为止世界上最强大的图像编辑软件&#xff0c;它已成为许多涉及图像处理的行业标准。软件技术一天行千里&#xff0c; Photoshop也在不断更新&#xff0c;从1990年开始发布&#xff0c; photoshop1.0到最新的 2018Photoshop... 几乎每隔一年&#xff0c;Photosho…

Apache 辅助系统工具

一丶Apache Sqoop 1.Sqoop的介绍&#xff1a; Sqoop的工作机制是将导入或者导出的命令翻译成MapReduce实现&#xff0c;Sqoop可以理解为&#xff1a;SQL到Hadoop或者Hadoop到SQL 2.Sqoop的安装 配置文件修改&#xff1a; cd $SQOOP_HOME/conf mv sqoop-env-template.sh sqo…

Dubbo Consumer 对Provider的protocol选择逻辑

一、简介 dubbo consumer 如果没有指定protocol参数&#xff0c;默认会将provider注册的protocol provider url都获取并转换为Invoker放到Directory中。如果consumer指定了protocol则会在Directory中选择相应的protocol provider url转换为Invoker放在Directory中。 二、代码分…

力扣hot100 柱状图中最大的矩形 单调栈

Problem: 84. 柱状图中最大的矩形 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考地址 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public static int largestRectangleArea(int[] height){Stack&l…

【英语趣味游戏】填字谜(Crossword)第1天

谜题出处 柯林斯字谜大全&#xff08;6&#xff09;&#xff0c;Collins——Big Book of Crosswords&#xff08;Book 6&#xff09; Puzzle Number: 114 本期单词 横向 1、Situation involving danger (4) 包含危险的情境&#xff0c;4个字母 答案&#xff1a;Risk&#xff…

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条件测试表达式…

揭秘IP地址定位:了解如何通过IP地址追踪你的位置

在互联网时代&#xff0c;我们的每一次在线活动都留下了痕迹&#xff0c;而IP地址则是其中一个关键的标识。IP地址定位技术的发展使得我们能够通过IP地址追踪设备的位置&#xff0c;这在某些情况下对于服务提供商、广告商和甚至研究人员来说可能是有用的。本文将深入探讨IP地址…

webassembly003 whisper.cpp的python绑定实现+Cython+Setuptools的GUI程序

CODE python端的绑定和本文一样&#xff0c;还需要将cdef char* LANGUAGE b’en’改为中文zh&#xff08;也可以在函数中配置一个参数修改这个值&#xff09;。ps:本来想尝试cdef whisper_context* whisper_init_from_file_with_params_no_state(char*, whisper_full_params)…

构建中国人自己的私人GPT—支持中文

上一篇已经讲解了如何构建自己的私人GPT&#xff0c;这一篇主要讲如何让GPT支持中文。 privateGPT 本地部署目前只支持基于llama.cpp 的 gguf格式模型&#xff0c;GGUF 是 llama.cpp 团队于 2023 年 8 月 21 日推出的一种新格式。它是 GGML 的替代品&#xff0c;llama.cpp 不再…

数字图像处理(实践篇)二十八 使用OpenCV Python中的K-means对图像进行颜色量化处理

目录 1 颜色量化 2 实践 在某些时候,不可避免的某些设备只能生成有限数量的颜色。因此需要执行颜色量化。选择使用cv2.kmeans()函数对颜色量化应用k-means聚类。 1 颜色量化 使用K-means聚类在图像中实现颜色量化的步骤如下: ① 导入依赖库