Typescript基础知识(类型拓宽、类型缩小)

系列文章目录

引入一:Typescript基础引入(基础类型、元组、枚举)
引入二:Typescript面向对象引入(接口、类、多态、重写、抽象类、访问修饰符)
第一章:Typescript基础知识(Typescript介绍、搭建TypeScript环境、基本数据类型)
第二章:Typescript常用类型(任意值any、数组Array、函数Function、元组Tuple、类型推论、联合类型)
第三章:Typescript基础知识(类型断言、类型别名、字符串字面量类型、枚举、交叉类型)
第四章:


文章目录

  • 系列文章目录
    • 一、类型拓宽
      • 1.1 什么是类型拓宽
      • 1.2 如何控制类型拓宽
        • 1.2.1 const 控制类型拓宽
        • 1.2.2 提供显式类型注释
        • 1.2.3 使用 const 断言
    • 二、类型缩小


一、类型拓宽

1.1 什么是类型拓宽

所有通过 let 或 var 定义的变量、函数的形参、对象的非只读属性,如果满足指定了初始值且未添加类型注解的条件,那么它们推断出来的类型就是指定的初始值字面量类型拓宽后的类型,这就是字面量类型拓宽。(第三章文章有提及过一嘴)

下面我们通过字符串字面量的示例来理解一下字面量类型拓宽:

  let str = 'this is string'; // 类型是 stringlet strFun = (str = 'this is string') => str; // 类型是 (str?: string) => string;const specifiedStr = 'this is string'; // 类型是 'this is string'let str2 = specifiedStr; // 类型是 'string'let strFun2 = (str = specifiedStr) => str; // 类型是 (str?: string) => string;

实际上,除了字面量类型拓宽之外,TypeScript 对某些特定类型值也有类似类型拓宽的设计,比如对 null 和 undefined 的类型进行拓宽,通过 let、var 定义的变量如果满足未显式声明类型注解且被赋予了 null 或 undefined 值,则推断出这些变量的类型是 any:

  let x = null; // 类型拓宽成 anylet y = undefined; // 类型拓宽成 any

注意:在严格模式下,一些比较老的版本中(2.0)null 和 undefined 并不会被拓宽成“any”。

1.2 如何控制类型拓宽

  • 案例一

我们先来看一个示例,如下代码所示:

interface Vector {x: number;y: number;z: number;
}function getComponent(vector: Vector, axis: "x" | "y" | "z") {return vector[axis];
}let x = "x";
let vec = { x: 1, y: 2, z: 3 };
getComponent(vec, x); // Error:类型“string”的参数不能赋给类型“"x" | "y" | "z"”的参数。

在这里插入图片描述
上述代码中,为什么会出现错误呢?通过 TypeScript 的错误提示消息,我们知道是因为变量 x 的类型被推断为 string 类型,而 getComponent 函数它的第二个参数有一个更具体的字面量类型。这在实际场合中被拓宽了,所以导致了一个错误。

1.2.1 const 控制类型拓宽

如果用 const 而不是 let 声明一个变量,那么它的类型会更窄。事实上,使用 const 可以帮助我们修复案例一例子中的错误:

const x = "x"; // type is "x" 
let vec = { x: 10, y: 20, z: 30 };
getComponent(vec, x); // OK

因为 x 不能重新赋值,所以 TypeScript 可以推断更窄的类型,就不会在后续赋值中出现错误。
然而,const 对于对象和数组,仍然会存在问题

  • 案例二

以下这段代码在 JavaScript 中是没有问题的:

const obj = { x: 1,
}; obj.x = 6; 
obj.x = '6';obj.y = 8;
obj.name = 'semlinker';

但是在TypeScrip的环境中最后三局会出现错误:

const obj = { x: 1,
};obj.x = 6; // OK // Type '"6"' is not assignable to type 'number'.
obj.x = '6'; // Error// Property 'y' does not exist on type '{ x: number; }'.
obj.y = 8; // Error// Property 'name' does not exist on type '{ x: number; }'.
obj.name = 'semlinker'; // Error

上述代码中,对于 obj 的类型来说,它可以是 {readonly x:1} 类型,或者是更通用的 {x:number} 类型。当然也可能是 {[key: string]: number} 或 object 类型。TypeScript 的拓宽算法会将对象其内部属性赋值给 let 关键字声明的变量,进而来推断其属性的类型。因此 obj 的类型为 {x:number} 。你可以将 obj.x 赋值给其他 number 类型的变量,但是它还会阻止你添加其他属性。

1.2.2 提供显式类型注释

如果用给let声明的变量设置显示的类型注释,也可以修复案例一例子中的错误:

let x: "x" = "x"; // type is "x" 
let vec = { x: 10, y: 20, z: 30 };
getComponent(vec, x); // OK

基于字面量类型拓宽的条件,我们可以通过添加显示类型注解控制类型拓宽行为。

  const specifiedStr: 'this is string' = 'this is string'; // 类型是 '"this is string"'let str2 = specifiedStr; // 即便使用 let 定义,类型是 'this is string'

1.2.3 使用 const 断言

不要将const 断言与 let 和 const 混淆,后者在值空间中引入符号。这是一个纯粹的类型级构造。让我们来看看以下变量的不同推断类型:

// Type is { x: number; y: number; }
const obj1 = { x: 1, y: 2 
}; // Type is { x: 1; y: number; }
const obj2 = {x: 1 as const,y: 2,
}; // Type is { readonly x: 1; readonly y: 2; }
const obj3 = {x: 1, y: 2 
} as const;

当你在一个值之后使用 const 断言时,TypeScript 将为它推断出最窄的类型,没有拓宽。当然你也可以对数组使用 const 断言:

/ Type is number[]
const arr1 = [1, 2, 3]; // Type is readonly [1, 2, 3]
const arr2 = [1, 2, 3] as const;

二、类型缩小

在 TypeScript 中,我们可以通过某些操作将变量的类型由一个较为宽泛的集合缩小到相对较小、较明确的集合,这就是类型缩小

比如,我们可以使用类型守卫将函数参数的类型从 any 缩小到明确的类型:

  let func = (anything: any) => {if (typeof anything === 'string') {return anything; // 类型是 string } else if (typeof anything === 'number') {return anything; // 类型是 number}return null;};

同样,我们可以使用类型守卫将联合类型缩小到明确的子类型,示例如下:

{let func = (anything: string | number) => {if (typeof anything === 'string') {return anything; // 类型是 string } else {return anything; // 类型是 number}};
}

我们也可以通过字面量类型等值判断(===)或其他控制流语句(包括但不限于 if、三目运算符、switch 分支)将联合类型收敛为更具体的类型,如下代码所示:

{type Goods = 'pen' | 'pencil' |'ruler';const getPenCost = (item: 'pen') => 2;const getPencilCost = (item: 'pencil') => 4;const getRulerCost = (item: 'ruler') => 6;const getCost = (item: Goods) =>  {if (item === 'pen') {return getPenCost(item); // item => 'pen'} else if (item === 'pencil') {return getPencilCost(item); // item => 'pencil'} else {return getRulerCost(item); // item => 'ruler'}}
}

在上述 getCost 函数中,接受的参数类型是字面量类型的联合类型,函数内包含了 if 语句的 3 个流程分支,其中每个流程分支调用的函数的参数都是具体独立的字面量类型。
那为什么类型由多个字面量组成的变量 item 可以传值给仅接收单一特定字面量类型的函数 getPenCost、getPencilCost、getRulerCost 呢?这是因为在每个流程分支中,编译器知道流程分支中的 item 类型是什么。比如 item === ‘pencil’ 的分支,item 的类型就被收缩为“pencil”。
事实上,如果我们将上面的示例去掉中间的流程分支,编译器也可以推断出收敛后的类型,如下代码所示:

  const getCost = (item: Goods) =>  {if (item === 'pen') {item; // item => 'pen'} else {item; // => 'pencil' | 'ruler'}}

一般来说 TypeScript 非常擅长通过条件来判别类型,但在处理一些特殊值时要特别注意 —— 它可能包含你不想要的东西!例如,以下从联合类型中排除 null 的方法是错误的:

const el = document.getElementById("foo"); // Type is HTMLElement | null
if (typeof el === "object") {el; // Type is HTMLElement | null
}

因为在 JavaScript 中 typeof null 的结果是 “object” ,所以你实际上并没有通过这种检查排除 null 值。除此之外,false的原始值也会产生类似的问题:

function foo(x?: number | string | null) {if (!x) {x; // Type is string | number | null | undefined}
}

因为空字符串和 0 都属于 false值,所以在分支中 x 的类型可能是 string 或 number 类型。帮助类型检查器缩小类型的另一种常见方法是在它们上放置一个明确的 “标签”:

interface UploadEvent {type: "upload";filename: string;contents: string;
}interface DownloadEvent {type: "download";filename: string;
}type AppEvent = UploadEvent | DownloadEvent;function handleEvent(e: AppEvent) {switch (e.type) {case "download":e; // Type is DownloadEvent break;case "upload":e; // Type is UploadEvent break;}
}

这种模式也被称为 ”标签联合“ 或 ”可辨识联合“,它在 TypeScript 中的应用范围非常广。

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

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

相关文章

elementPlus——图标引入+批量注册全局组件——基础积累

因为我们要根据路由配置对应的图标,也要为了后续方便更改。因此我们将所有的图标注册为全局组件。(使用之前将分页器以及矢量图注册全局组件的自定义插件)(所有图标全局注册的方法element-plus文档中已给出) 全局注册…

Docker-compose详解和LNMP搭建实战

目录 一、Docker-compose简介 1.前言 2.概述 二、Docker-compose安装 安装源获取 安装包下载 三、YAML文件格式及编写注意事项 1.简介 2.使用方法 四、Docker Compose 常用命令 五、Docker Compose 配置常用字段 六、Docker-compose搭建LNMP实战 一、Docker-compose…

MySQL的安装以及卸载

下载官网 https://www.mysql.com/ 切到下载tab页 找到 MySQL Community Server 或者 MySQL Community (GPL) Downloads --> MySQL Community Server 点击download按钮: 点击download进入下载页面选择No thanks, just start my download就可以开始下载了。 下…

【ARM】Day5 uart总线, LED点亮实验(C语言实现)

1. 思维导图 2. LED点灯实验(C语言实现) gpio.h #ifndef _LED_H__ //防止头文件重复包含_ #define _LED_H__//RCC_MP_AHB4ENSETR寄存器封装 #define RCC_MP_AHB4ENSETR (*(volatile unsigned int*)0x50000A28)//GPIO使用封装结构体 typedef struct{v…

【Linux】进程优先级

一、基本概念 Hello,大家好。本文我们要来介绍的是有关Linux下【进程优先级】,首先我们要了解的是其基本概念 在 Linux基础篇之权限 一文中我们有谈到过什么是权限,在Linux下有权限和无权限的区别在哪里。那现在的话我们就要来对比一下【权限…

js 的正则表达式(二)

1.正则表达式分类: 正则表达式分为普通字符和元字符。 普通字符: 仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。也就是说普通字符只能够匹配字符串中与它们相同的字符。 元字符: 是一些具有特殊含…

一文科普,配资门户网是什么?

配资门户网是一个为投资者提供配资服务的平台。配资是指通过借用他人资金进行投资交易的一种金融操作方式。配资门户网作为一个线上平台,为投资者提供了方便、快捷的配资服务。 配资门户网提供了多种不同的配资方案,以满足不同投资者的需求。投资者可以…

通过Git使用GitHub

目录 一、建立个人仓库 二、配置SSH密钥 三、克隆仓库代码 四、推送代码到个人仓库 五、代码拉取 一、建立个人仓库 1.建立GitHub个人仓库,首先注册GitHub用户。注册好了之后,打开用户的界面 然后就是配置问题 配置好后拉到最下方点击create repos…

opencv进阶07-支持向量机cv2.ml.SVM_create()简介及示例

支持向量机(Support Vector Machine,SVM)是一种二分类模型,目标是寻找一个标准(称为超平面)对样本数据进行分割,分割的原则是确保分类最优化(类别之间的间隔最大)。当数据…

next.js 创建 react ant design ts 项目

环境说明:next.js 官方文档要求node版本在16.8以上。笔者使用的 node版本是16.20.1,不要使用16.13.0,笔者在使用 node16.13.0环境时创建的 react 项目点击事件无效 next.js官网截图 next.js 官网:https://nextjs.org/ react 官网…

【UniApp开发小程序】商品详情展示+评论、评论展示、评论点赞+商品收藏【后端基于若依管理系统开发】

文章目录 界面效果界面实现工具js页面日期格式化 后端收藏ControllerServicemapper 评论ControllerServiceMapper 商品Controller 阅读Service 界面效果 【说明】 界面中商品的图片来源于闲鱼,若侵权请联系删除 【商品详情】 【评论】 界面实现 工具js 该工…

uniapp 微信小程序 绘制海报,长按图片分享,保存海报

uView UI 2.0 dcloud 插件市场地址 弹窗海报源码 <template><!-- 推荐商品弹窗 --><u-popup :show"haibaoShow" mode"center" round26rpx z-index10076 bgColortransparent safeAreaInsetTop close"goodsclose"><image …

WPF入门到精通:1.新建项目及项目结构

WPF&#xff08;Windows Presentation Foundation&#xff09;是一种用于创建 Windows 应用程序的技术&#xff0c;它可以通过 XAML&#xff08;Extensible Application Markup Language&#xff09;和 C# 或其他 .NET 语言来实现。WPF 提供了许多强大的 UI 控件和样式&#xf…

Azure虚拟网络对等互连

什么是Azure虚拟网络对等互联 Azure虚拟网络对等互联&#xff08;Azure Virtual Network peering&#xff09;是一种连接两个虚拟网络的方法&#xff0c;使得这两个虚拟网络能够在同一地理区域内进行通信。它通过私有IP地址在虚拟网络之间建立网络连接&#xff0c;不论是在同一…

星际争霸之小霸王之小蜜蜂(四)--事件监听-让小蜜蜂动起来

目录 前言 一、监听按键并作出判断 二、持续移动 三、左右移动 总结&#xff1a; 前言 今天开始正式操控我们的小蜜蜂了&#xff0c;之前学java的时候是有一个函数监听鼠标和键盘的操作&#xff0c;我们通过传过来不同的值进行判断&#xff0c;现在来看看python是否一样的实现…

SpringMVC之异常处理

SpringMVC之异常处理 异常分为编译时异常和运行时异常&#xff0c;编译时异常我们trycatch捕获&#xff0c;捕获后自行处理&#xff0c;而运行时异常是不可预期的&#xff0c;就需要规范编码来避免&#xff0c;在SpringMVC中&#xff0c;不管是编译异常还是运行时异常&#xff…

2023面试八股文 ——Java基础知识

Java基础知识 一.Java概述何为编程什么是Javajdk1.5之后的三大版本JVM、JRE和JDK的关系什么是跨平台性&#xff1f;原理是什么Java语言有哪些特点什么是字节码&#xff1f;采用字节码的大好处是什么什么是Java程序的主类&#xff1f;应用程序和小程序的主类有何不同&#xff1f…

漏洞指北-VulFocus靶场专栏-初级01

漏洞指北-VulFocus靶场专栏-初级 初级001 &#x1f338;海洋CMS代码执行&#xff08;CNVD-2020-22721&#x1f338;step1&#xff1a;进入后台页面 账号密码&#xff1a;admin amdinstep2&#xff1a;点击系统&#xff0c;点击后台IP安全设置,关闭step3 启动burpsuite&#xff…

一百五十九、Kettle——Kettle9.2通过配置Hadoop clusters连接Hadoop3.1.3(踩坑亲测、附流程截图)

一、目的 由于kettle的任务需要用到Hadoop&#xff08;HDFS&#xff09;&#xff0c;所以就要连接Hadoop服务。 之前使用的是kettle9.3&#xff0c;由于在kettle新官网以及博客百度等渠道实在找不到shims的驱动包&#xff0c;无奈换成了kettle9.2&#xff0c;kettle9.2的安装…

设计模式之迭代器模式(Iterator)的C++实现

1、迭代器模式的提出 在软件开发过程中&#xff0c;操作的集合对象内部结构常常变化&#xff0c;在访问这些对象元素的同时&#xff0c;也要保证对象内部的封装性。迭代器模式提供了一种利用面向对象的遍历方法来遍历对象元素。迭代器模式通过抽象一个迭代器类&#xff0c;不同…