阮一峰《TypeScript 教程》学习笔记一类型系统、数组、元祖

news2024/11/15 7:27:50

阮一峰《TypeScript 教程》
本篇文章主要记录浏览阮一峰《TypeScript 教程》书籍的过程中本人不会的一些TypeScript的用法。当然,可以说,我都不会哈哈哈,不过有的用法比较奇葩的我就不记录了,只记录我觉得项目中会用到,比较有实用价值的知识点。不得不说,阮老师写的真的是太好了,清晰易懂,而且特别详细。
typescript 在线编译的网站:
https://www.typescriptlang.org/play

类型系统

any、undefined、null

首字母大写的NumberStringBoolean等在 JavaScript 语言中都是内置对象,而不是类型名称。
undefined 和 null 既可以作为值,也可以作为类型

如果没有声明类型的变量,被赋值为undefinednull,它们的类型会被推断为any

let a = undefined;   // any
const b = undefined; // any
let c = null;        // any
const d = null;      // any

任何其他类型的变量都可以赋值为undefinednull。这并不是因为undefinednull包含在number类型里面,而是故意这样设计,任何类型的变量都可以赋值为undefinednull,以便跟 JavaScript 的行为保持一致。

let age:number = 24;
age = null;      // 正确
age = undefined; // 正确

但是这种情况,在编译阶段不报错,在运行阶段可能会报错,例如

const obj:object = undefined;
obj.toString() // 编译不报错,运行就报错

为了避免这种情况,及早发现错误,TypeScript 提供了一个编译选项strictNullChecks。只要打开这个选项,undefinednull就不能赋值给其他类型的变量(除了any类型和unknown类型)。

这个选项在配置文件tsconfig.json的写法如下:

{
  "compilerOptions": {
    "strictNullChecks": true
    // ...
  }
}

Object与object类型

大写的 Object 类型代表 Javascript 里面的广义对象,所有可以转义成对象的数据,都属于 Object 类型,这囊括了几乎所有的值。

let obj:Object;
obj = true;
obj = 'hi';
obj = 1;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;

以上都是正确的赋值。事实上,除了 nullundefined,其他的数据都可以赋值给 Object 类型。
另外,空对象 {}Object 的简写形式,所以 Object 常常使用 {} 代替

let obj:{};
obj = true;
obj = 'hi';
obj = 1;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;

无所不包的 Object 对象既不符合直觉,也不利于使用。所以尽量不要用…
小写的 object 类型指的是狭义的对象,即可以用字面量表示的对象,包含数组、对象、函数,不包含原始值

let obj:object;
obj = { foo: 123 };
obj = [1, 2];
obj = (a:number) => a + 1;
obj = true; // 报错
obj = 'hi'; // 报错
obj = 1; // 报错

值类型

TypeScript 规定,单个值也是一种类型,称为“值类型”。下面示例,变量x的类型是字符串hello,导致它只能赋值为这个字符串,赋值为其他字符串就会报错

let x:'hello';
x = 'hello'; // 正确
x = 'world'; // 报错

TypeScript 推断类型时,遇到const命令声明的变量,如果代码里面没有注明类型,就会推断该变量是值类型

// x 的类型是 "https"
const x = 'https';
// y 的类型是 string
const y:string = 'https';

联合类型

联合类型(union types)指的是多个类型组成的一个新类型,使用符号|表示。

联合类型A|B表示,任何一个类型只要属于AB,就属于联合类型A|B

let x:string|number;
x = 123; // 正确
x = 'abc'; // 正确

上面示例中,变量x就是联合类型string|number,表示它的值既可以是字符串,也可以是数值。

如果一个变量有多种类型,读取该变量时,往往需要进行“类型缩小”(type narrowing),区分该值到底属于哪一种类型,然后再进一步处理。

function printId(
  id:number|string
) {
  if (typeof id === 'string') {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}

实际上,联合类型本身可以看成是一种“类型放大”(type widening),处理时就需要“类型缩小”(type narrowing)。

交叉类型

交叉类型A&B表示,任何一个类型必须同时属于AB,才属于交叉类型A&B,即交叉类型同时满足AB的特征。交叉类型的主要用途是表示对象的合成。

let obj:
  { foo: string } &
  { bar: string };
obj = {
  foo: 'hello',
  bar: 'world'
};

上面示例中,变量obj同时具有属性foo和属性bar

交叉类型常常用来为对象类型添加新属性。

type A = { foo: number };
type B = A & { bar: number };

type 命令

type命令用来定义一个类型的别名。

type Age = number;
let age:Age = 55;

上面示例中,type命令为number类型定义了一个别名Age。这样就能像使用number一样,使用Age作为类型。别名可以让类型的名字变得更有意义,也能增加代码的可读性,还可以使复杂类型用起来更方便,便于以后修改变量的类型。

别名不允许重名。

别名的作用域是块级作用域

别名支持使用表达式,也可以在定义一个别名时,使用另一个别名,即别名允许嵌套。

type World = "world";
type Greeting = `hello ${World}`;

type命令属于类型相关的代码,编译成 JavaScript 的时候,会被全部删除。

typeof 运算符

typeof 运算符是一个一元运算符,返回一个字符串,代表操作数的类型

JavaScript 里面,typeof运算符只可能返回八种结果,而且都是字符串。

typeof undefined; // "undefined"
typeof true; // "boolean"
typeof 1337; // "number"
typeof "foo"; // "string"
typeof {}; // "object"
typeof parseInt; // "function"
typeof Symbol(); // "symbol"
typeof 127n // "bigint"

TypeScript 将typeof运算符移植到了类型运算,它的操作数依然是一个值,但是返回的不是字符串,而是该值的 TypeScript 类型。

const a = { x: 0 };
type T0 = typeof a;   // { x: number }
type T1 = typeof a.x; // number

TypeScript 规定,typeof 的参数只能是标识符,不能是需要运算的表达式,typeof命令的参数不能是类型

块级类型声明

TypeScript 支持块级类型声明,即类型可以声明在代码块(用大括号表示)里面,并且只在当前代码块有效。

if (true) {
  type T = number;
  let v:T = 5;
} else {
  type T = string;
  let v:T = 'hello';
}

类型的兼容

TypeScript 的类型存在兼容关系,某些类型可以兼容其他类型。如果类型A的值可以赋值给类型B,那么类型A就称为类型B的子类型(subtype)。类型number就是类型number|string的子类型。

TypeScript 的数组类型

JavaScript 数组在 TypeScript 里面分成两种类型,分别是数组(array)和元组(tuple)。

数组特征:元素的类型相同,但是数量不定

数组有两种写法

let arr:number[] = [1, 2, 3];

数组arr的类型是number[],其中number表示数组成员类型是number

如果数组成员的类型比较复杂,可以写在圆括号里面。

let arr:(number|string)[];

数组类型的第二种写法是使用 TypeScript 内置的 Array 接口。

let arr:Array<number> = [1, 2, 3];
let arr:Array<number|string>;

用尖括号的这种写法本质上属于泛型。

数组的类型推断

如果数组初始的值是空的,那么后续赋值过程会根据数组中元素的值自动推断类型,并且会自动更新类型推断

// 推断为 any[]
const arr = [];
// 推断类型为 number[]
arr.push(123);
// 推断类型为 (string | number)[]
arr.push('abc');

但是,类型推断的自动更新只发生初始值为空数组的情况。如果初始值不是空数组,类型推断就不会更新。

// 推断类型为 number[]
const arr = [123];
arr.push('abc'); // 报错

只读数组

TypeScript 允许声明只读数组,即不允许变动数组成员,方法是在数组类型前面加上readonly关键字。

const arr:readonly number[] = [0, 1];
arr[1] = 2; // 报错
arr.push(3); // 报错
delete arr[0]; // 报错

TypeScript 将readonly number[]number[]视为两种不一样的类型,后者是前者的子类型。

这是因为只读数组没有pop()push()之类会改变原数组的方法,所以number[]的方法数量要多于readonly number[],这意味着number[]其实是readonly number[]的子类型。

readonly关键字不能与数组的泛型写法一起使用。

// 报错
const arr:readonly Array<number> = [0, 1];

实际上,TypeScript 提供了两个专门的泛型,用来生成只读数组的类型。

const a1:ReadonlyArray<number> = [0, 1];
const a2:Readonly<number[]> = [0, 1];

只读数组还有一种声明方法,就是使用“const 断言”。

const arr = [0, 1] as const;
arr[0] = [2]; // 报错

上面示例中,as const告诉 TypeScript,推断类型时要把变量arr推断为只读数组,从而使得数组成员无法改变。

多维数组

TypeScript 使用T[][]的形式,表示二维数组,T是最底层数组成员的类型。

var multi:number[][] =[[1,2,3], [23,24,25]];

表示 multi 是一个二维数组,元素的数据类型为 number

TypeScript 的元组类型

元祖是 TypeScript 特有的数据类型,表示元素的数据类型可以不同的数组。
元祖必须声明每个成员的类型

const s:[string, string, boolean] = ['a', 'b', true];

元祖和数组的区分方式就是:元祖的成员类型在方括号里面,数组的成员类型在方括号外面。
使用元祖时,必须设置成员类型,否则就会被推断为一个数组,例如下面的写法:

// a 的类型为 (number | boolean)[]
let a = [1, true];

会被自动推断为成员类型为 (number | boolean) 的数组。
元祖还可以在结尾的成员类型后面加上问号,表示这个成员可有可无

type myTuple = [
  number,
  number,
  number?,
  string?
];

上面这个元祖,表示最后两个成员是可选的。
由于需要声明每个成员的类型,元祖在大多数情况下成员数量都很少。元祖的成员数量在类型声明的时候就能看出来,超过声明的数量的话就会报错。
但是,元祖的成员类型声明可以使用 … 扩展运算符来声明不限制成员数量的元祖

type NamedNums = [
  string,
  ...number[]
];
const a:NamedNums = ['A', 1, 2];
const b:NamedNums = ['B', 1, 2, 3];

上面这个元祖,第一个元素必须是字符串,后面可以有若干个数字类型的成员。
扩展运算符在元素的任意位置都可以,但它的后面只能跟数组或元祖

type t1 = [string, number, ...boolean[]];
type t2 = [string, ...boolean[], number];
type t3 = [...boolean[], string, number];

只读元祖

元祖也可以是只读的。只读元祖有下面两种写法:

// 写法一
type t = readonly [number, string]
// 写法二
type t = Readonly<[number, string]>

两种写法都可以得到只读元祖,其中写法二是泛型的写法,用到了工具类型 Readonly<T>
跟数组一样,只读元祖是元祖的父类型,因为元祖比只读元祖拥有更多的方法。因此,元祖可以替代只读元祖,只读元祖不能替代元祖。

type t1 = readonly [number, number];
type t2 = [number, number];
let x:t2 = [1, 2];
let y:t1 = x; // 正确
x = y; // 报错

上面代码中,t1 是只读元祖,t2 是普通元祖。可以把普通元祖赋值给只读元祖,但不可以把只读元祖赋值给普通元祖。

成员数量的推断

如果没有扩展运算符,只有普通成员和可选成员,元祖可以推断出成员的数量。
看以下两段代码:

function f(point: [number, number]) {
  if (point.length === 3) {  // 报错
    // ...
  }
}
function f(
  point:[number, number?, number?]
) {
  if (point.length === 4) {  // 报错
    // ...
  }
}

上述两段代码,由于元祖会自动推断成员数量,判断出数量不可能等于进行判断的数值,代码会直接报错。
如果使用了扩展运算符,元祖就无法推断成员数量了。

const myTuple:[...string[]]
  = ['a', 'b', 'c'];
if (myTuple.length === 4) { // 正确
  // ...
}

如果使用了扩展运算符,其实是会被当成数组看待,而数组的成员数量是不定的,所以此时无法推断成员数量。
一旦扩展运算符使元祖的成员数量无法确定时,typescript 会把元祖当成数组看待。

扩展运算符与成员数量

扩展运算符会将数组转换成一个用逗号分割的序列,由于数组的成员数量是不定的,使用扩展运算符展开数组的时候,展开的结果的成员数量也是不定的。
这就会导致在函数调用时,如果函数接收的参数数量是一定的,但是传参的时候使用了扩展运算符,在编译的时候就会报错

const arr = [1, 2];
function add(x:number, y:number){
  // ...
}
add(...arr) // 报错

在这里插入图片描述
上述报错,原因是因为 add() 函数只能接受两个参数,而使用扩展运算符传参,typescript 会认为参数数量不确定,因此就报错了。
有些函数是可以接受任意数量的参数的,这种就不会报错,比如 console.log()
解决这个问题有两个办法

  • 把成员数量不定的数组,写成成员数量确定的元祖
const arr:[number, number] = [1, 2];
function add(x:number, y:number){
  // ...
}
add(...arr) // 正确
  • 使用 as const 断言
const arr = [1, 2] as const;

使用这种写法,arr 的类型是 readonly [1,2] 只读类型,可以当做数组,也可以当做元祖。

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

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

相关文章

Python类型检查器库之typeshed使用详解

概要 在 Python 开发中,类型检查器(如 mypy)和 IDE(如 PyCharm)能够显著提高代码的可读性和可维护性。然而,Python 本身是一种动态类型语言,标准库和第三方库通常没有类型注解。这就需要一个包含这些库类型信息的资源库,以便类型检查器能够正常工作。Typeshed 正是这样…

【Python】学习Python的流程,有这一篇资料就够了,带你深入了解Python!!!

点击免费领取《CSDN大礼包》&#xff1a;Python入门到进阶资料 & 实战源码 & 兼职接单方法 安全链接免费领取 书籍推荐 《Python编程:从入门到实践》 作者&#xff1a;埃里克马瑟斯&#xff08;Eric Matthes&#xff09;特点&#xff1a;通过实际项目引导读者学习Pyt…

前端生成验证码

一.效果图 二、实现 2.1html <template><div class"outer-box"><div class"code-box"><div class"inout-code"><label class"label" for"inputCode">验证码&#xff1a;</label><…

内存管理篇-05物理页面的迁移类型migratetype

本节内容依旧是对上节课伙伴系统的补充&#xff0c;主要介绍了新版伙伴系统的页面迁移相关的内容 为什么要引入页面迁移类型&#xff1f;新版本伙伴系统针对老版本的伙伴系统的升级改进。主要优化memory compaction内存碎片整理的过程。 页面迁移实际上就是伙伴系统中free_area…

第一次get到学习大模型的沉浸感和成就感 ,大模型入门必看!!<大模型应用开发极简入门>(PDF分享)

人工智能大潮已来&#xff0c;不加入就可能被淘汰。就好像现在职场里谁不会用PPT和excel一样&#xff0c;基本上你见不到。 而大模型是人工智能代表&#xff0c;潜力与使用方式有关。使用好大模型可提高效率&#xff0c;让人获得更好的待遇和更多机会。所以今天给大家推荐一本…

【STM32】中断

中断与GPIO一样是STM32中非常常用的东西&#xff0c;以我一个写上位机的人的理解&#xff0c;它相当于上位机程序中多线程的地位&#xff0c;因为多线程和中断都是为了提高某些响应的实时性。悄悄说一句&#xff0c;我觉得中断在一定程度上比多线程还好用。哈哈哈哈哈哈哈~ 大部…

竖图秒变横屏壁纸!AI绘画SD最强控制插件ControlNet教程,一键实现图片的扩展式填充真的太强了!

大家好&#xff0c;我是画画的小强 经常用PhotoShop作图的人都知道&#xff0c;自从去年PS2023beta版的发布&#xff0c;引入了AI绘画给广大创作者带来了不小的惊喜&#xff0c;最主要的就是它的“创成式填充”&#xff0c;通过现有图片可以进行扩展和延展&#xff0c;其强大的…

Vue2 中 $set 用法详细讲解

给变量重复赋值&#xff0c;Vue 监测不到数据变化。这个时候&#xff0c;双向绑定就失效了。 1、为什么要用set&#xff1f; 在vue中&#xff0c;并不是任何时候数据都是双向绑定的。 在官方文档中&#xff0c;有这样一段话&#xff0c;如下&#xff1a; 从文档得知&#xff…

wpf 定制 个性圆角信息面板

先上图&#xff1a; 代码实现&#xff1a; <Canvas Grid.Column"1"><Border Background"#5665F4" BorderBrush"#5665F4" BorderThickness"0.5" CornerRadius"10,10,10,30"Width"180" Height"165&qu…

CSS背景与边框——WEB开发系列18

网页设计中&#xff0c;背景和边框是用于提升视觉效果的关键元素。CSS&#xff08;层叠样式表&#xff09;提供了丰富的功能来定制这些视觉效果&#xff0c;确保网页看起来既美观又符合设计需求。 一、背景样式 背景样式在网页设计中起着至关重要的作用。它们可以用来增加页面…

qt在ui上面给QWidget设置布局

如图所示&#xff0c;我们无法给QWidget设置布局 拖拽一个GridLayout进来&#xff0c;设置QWidget布局&#xff0c;然后删掉该GridLayout 结果QWidget设置网格布局

广州自闭症全托管学校-正规儿童康复中心

在繁华的广州城&#xff0c;有一个特殊的地方&#xff0c;那就是致力于自闭症儿童康复的星贝育园。星贝育园不仅在广州拥有校区&#xff0c;在浙江也有三个校区&#xff0c;它就像一盏明灯&#xff0c;照亮了自闭症儿童和他们家庭的前行之路。 走进星贝育园&#xff0c;你会感…

【CUDA编程笔记】thrust::device_vector<float> signal无法编译问题记录

thrust::device_vector signal无法编译问题记录 CUDA编程笔记 一、问题记录 正常编译时&#xff0c;无法编译 二、源码 #include <thrust/host_vector.h> #include <thrust/device_vector.h> #include <thrust/generate.h> #include <thrust/sort.…

22:【stm32】定时器三:输出比较

输出比较 1、简介2、标准库编程 1、简介 通过CCR里面的值和计数器CNT里面的值进行比较&#xff0c;然后输出高电平/单片机&#xff0c;进而产生需要的信号。 如上图所示&#xff0c;预分频器71&#xff0c;则最小单元为1us&#xff0c;自动重装器为999&#xff0c;则周期为1ms…

InternLM + LlamaIndex RAG 实践

1. 环境配置 首先创建一个虚拟环境 conda create -n llamaindex python3.10 为虚拟环境安装以下安装包 conda activate llamaindex conda install pytorch2.0.1 torchvision0.15.2 torchaudio2.0.2 pytorch-cuda11.7 -c pytorch -c nvidia pip install einops pip install …

Midjourney提示词-动物系列-65

A super cute little anthropomorphic,sheep of the Chinese Zodiac, wearing berets ,in a Hanfu in red style,standing, eyes,cute tail,super realistic,super detail,luxurious,elegant,Unreal Engine,octane render, 8K,VRAY super realistic Pixar Style, Tiny cute…

百数功能插件技术解析:审批流程设置与数据填写便捷性探讨

低代码平台作为一种创新的开发工具&#xff0c;正逐渐受到企业的青睐。为了进一步提升用户体验&#xff0c;满足用户多样化的需求&#xff0c;百数低代码平台开通了“有问必答”板块。 问题1&#xff1a;功能插件到底有什么用&#xff1f;都用在哪些位置&#xff1f; 功能插件…

【大模型系列】Flash-VStream(2024.06)

● Paper&#xff1a;https://arxiv.org/pdf/2406.08085v1 ● Github&#xff1a;https://github.com/IVG-SZ/Flash-VStream?tabreadme-ov-file#structure ● HuggingFace&#xff1a;https://huggingface.co/spaces/IVGSZ/Flash-VStream-demo ● Author&#xff1a;Haoji Zha…

line-height的使用场景

line-height:字面含义为行高&#xff0c;行高有三部分组成&#xff0c;分为内容高度&#xff0c;上间距&#xff0c;下间距。 可以看到文本在div盒子中的默认位置是左上角。此时文字部分的行高只有内容高度在支撑&#xff0c;上间距和下间距都是0。鼠标在字体上滑动时的蓝色部…

【重点】人工智能大语言模型技术发展研究报告2024|附下载

人工智能作为引领新一轮科技产业革命的战略性技术和新质生产力重要驱动力&#xff0c;正在引发经济、社会、文化等领域的变革和重塑。 2023 年以来&#xff0c;以ChatGPT、GPT-4 为代表的大模型技术的出台&#xff0c;因其强大的内容生成及多轮对话能力&#xff0c;引发全球新…