【TypeScript】阮一峰TypeScript教程笔记:基本用法、any 类型等、类型系统

2023-10-12 13:30

本文主要是介绍【TypeScript】阮一峰TypeScript教程笔记:基本用法、any 类型等、类型系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

是阮一峰 TypeScript 教程的笔记。笔记目录与教程目录一致。笔记不全,仅作自己学习使用,完整学习请看:阮一峰 TypeScript 教程或TypeScript官方文档。

文章目录

    • 简介
    • 基本用法
    • any 类型,unknown 类型,never 类型
      • any 类型
      • 类型推断问题
      • 污染问题
      • unknown 类型
      • never 类型
    • 类型系统
      • 基本类型
      • 包装对象类型
      • 包装对象类型与字面量类型
      • Object 类型与 object 类型
      • undefined 和 null 的特殊性
      • 值类型
      • 联合类型
      • 交叉类型
      • type 命令
      • typeof 运算符
      • 类型的兼容

简介

TypeScript 可以看成是 JavaScript 的超集(superset),即它继承了后者的全部语法,所有 JavaScript 脚本都可以当作 TypeScript 脚本(但是可能会报错),此外它再增加了一些自己的语法。

TypeScript 对 JavaScript 添加的最主要部分,就是一个独立的类型系统

类型系统

类型是人为添加的一种编程约束和用法提示。 目的:提早发现错误。

举个例子:

function addOne(n: number) {return n + 1;
}

若:addOne("hello");,在TypeScript中会报错,但在JS中不会。

动态类型与静态类型

JS是动态类型语言,TS是静态类型语言。

// 例一
let x = 1;
x = "hello";// 例二
let y = { foo: 1 };
delete y.foo;
y.bar = 2;

若在JS中,上面的代码就是正确的:约束性很弱,不利于提前发现代码错误。(无法提前知道某个属性在不在,或某个变量的数据类型
在TS中,上述代码会报错。TS为JS引入了静态类型特征

基本用法

类型声明

为JS变量加上类型声明。写法:标识符后“冒号+类型”。

变量:

let foo:string;

函数:

function toString(n: number): string {return String(n);
}

报错:

  • 变量的值与声明类型不一致
  • 变量未赋值就使用(在JS中,为赋值的变量返回undefined但不会报错)

类型推断

类型声明并不是必需的。若无,TS会自己推断类型。若变量赋值后更改为其他类型的值,跟推断的类型不一致,TS会报错。

let foo = 123;
foo = "hello"; // 报错

编译

JS的运行环境(浏览器和Node.js)不认识TS。因此,TS想要运行,要先转为JS,这个过程即编译

关于编译:编译时,会将类型声明和类型相关的代码全部删除,只留下能运行的 JavaScript 代码,并且不会改变 JavaScript 的运行结果。

关于类型检查:只是编译时的类型检查,而不是运行时的类型检查。

值与类型

“类型”是针对“值”的,可以视为是“值”的一个属性。每一个值在TS中都是有类型的。如,3是一个值,他的类型是number。

TypeScript 代码只涉及类型,不涉及值。所有跟“值”相关的处理,都由 JavaScript 完成。
这一点务必牢记。TypeScript 项目里面,其实存在两种代码,一种是底层的“值代码”,另一种是上层的“类型代码”。前者使用 JavaScript 语法,后者使用 TypeScript 的类型语法。

在TS的编译过程,其实就是把“类型代码”全部拿掉,只保留“值代码”。

TypeScript Playground

https://www.typescriptlang.org/play

any 类型,unknown 类型,never 类型

any 类型

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。

let x: any;x = 1; // 正确
x = "foo"; // 正确
x = true; // 正确
let x: any = "hello";x(1); // 不报错
x.foo = 100; // 不报错

上面示例中,变量x的值是一个字符串,但是把它当作函数调用,或者当作对象读取任意属性,TypeScript 编译时都不报错。原因就是x的类型是any,TypeScript 不对其进行类型检查。

因此,尽量避免使用any类型,否则就失去了使用 TypeScript 的意义。

实际开发中,any类型主要适用以下两个场合:

  1. 出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any。
  2. 为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。

类型推断问题

对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是any

使用letvar命令声明变量,但不赋值也不指定类型,是不会报错的。

var x; // 不报错
let y; // 不报错

建议使用let和var声明变量时,如果不赋值,就一定要显式声明类型,否则可能存在安全隐患。 如下面代码:用let声明后不赋值,则x类型推断为any。下面代码不会报错。

let x;x = 123;
x = { foo: "hello" };

污染问题

any类型除了关闭类型检查,还有一个很大的问题,就是它会 “污染” 其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错。

let x: any = "hello";
let y: number;y = x; // 不报错y * 123; // 不报错
y.toFixed(); // 不报错

上面示例中,变量x的类型是any,实际的值是一个字符串。变量y的类型是number,表示这是一个数值变量,但是它被赋值为x,这时并不会报错。然后,变量y继续进行各种数值运算,TypeScript 也检查不出错误,问题就这样留到运行时才会暴露。

污染其他具有正确类型的变量,把错误留到运行时,这就是不宜使用any类型的另一个主要原因。

unknown 类型

为了解决any类型“污染”其他变量的问题,TypeScript 3.0 引入了unknown类型。它与any含义相同,表示类型不确定,可能是任意类型,但是有一些限制。

与any的相同之处:所有类型的值都可以分配给unknown类型。
与any的不同之处:不能直接使用。

  • 不能直接赋值给其他类型的变量(除了any类型和unknown类型)
  • 不能直接调用unknown类型变量的方法和属性
  • unknown类型变量能进行的运算有限:比较运算(运算符=====!=!==||&&?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错

使用unknown的方法:“类型缩小”。即缩小unknown变量的类型范围,确保不会出错。

如:

let a: unknown = 1;if (typeof a === "number") {let r = a + 10; // 正确
}
let s: unknown = "hello";if (typeof s === "string") {s.length; // 正确
}

unknown可以看作是更安全的any。凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。

never 类型

TS引入“空类型”的概念,即该类型为空,不包含任何值。由于不存在任何属于“空类型”的值,所以该类型被称为never,即不可能有这样的值。

let x: never;

上面示例中,变量x的类型是never,就不可能赋给它任何值,否则都会报错。

使用场景:

  • 在一些类型运算之中,保证类型运算的完整性
  • 不可能返回值的函数

如果一个变量可能有多种类型(即联合类型),通常需要使用分支处理每一种类型。这时,处理所有可能的类型之后,剩余的情况就属于never类型。如:

function fn(x: string | number) {if (typeof x === "string") {// ...} else if (typeof x === "number") {// ...} else {x; // never 类型}
}

可以赋值给任意其他类型。

function f(): never {throw new Error("Error");
}let v1: number = f(); // 不报错
let v2: string = f(); // 不报错
let v3: boolean = f(); // 不报错

类型系统

基本类型

boolean、string、number、bigint、symbol、object、undefined、null。

undefined:表示未定义。
null:表示空(此处没有值)。

包装对象类型

booleanstringnumber有对应的包装对象类型:Boolean()String()Number()

在调用方法时,字符串会自动转为包装对象。(原本原始类型的值没有方法,对象才有)

"hello".charAt(1); // 'e'

当作构造函数使用时,才会返回包装对象。

const s = new String("hello");
typeof s; // 'object'
s.charAt(1); // 'e'

包装对象类型与字面量类型

每一个原始类型的值都有包装对象和字面量两种情况。

"hello"; // 字面量
new String("hello"); // 包装对象

大写类型同时包含包装对象和字面量两种情况,小写类型只包含字面量,不包含包装对象:

const s1: String = "hello"; // 正确
const s2: String = new String("hello"); // 正确const s3: string = "hello"; // 正确
const s4: string = new String("hello"); // 报错

建议只使用小写类型,不使用大写类型。

因为绝大部分使用原始类型的场合,都是使用字面量,不使用包装对象。而且,TypeScript 把很多内置方法的参数,定义成小写类型,使用大写类型会报错。如:

const n1: number = 1;
const n2: Number = 1;Math.abs(n1); // 1
Math.abs(n2); // 报错

内置的Math.abs的参数类型是小写的number,若传入大写的Number类型就会报错。

Object 类型与 object 类型

Object(大写)

所有可以转成对象的值,都是Object类型。除了undefinednull这两个值不能转为对象,其他任何值都可以赋值给Object类型。

空对象{}Object类型的简写形式。

object(小写)

不包含原始类型值,只包含对象、数组和函数。

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

建议总是使用小写类型object。大多数时候,我们使用对象类型,只希望包含真正的对象,不希望包含原始类型。

undefined 和 null 的特殊性

任何其他类型的变量都可以赋值为undefined或null。

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

这样做的原因是:以便跟 JavaScript 的行为保持一致。

有时候,这不是开发者想要的行为,也不利于发挥类型系统的优势。

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

为了避免这种情况,TypeScript 提供了一个编译选项strictNullChecks。打开它以后,undefinednull只能赋值给自身,或者any类型和unknown类型的变量。

值类型

单个值也是一种类型,称为“值类型”。值类型不能赋为其他值。

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

注意,const命令声明的变量,如果赋值为对象,并不会推断为值类型。const变量赋值为对象时,属性值是可以改变的。

联合类型

联合类型(union types)指的是多个类型组成的一个新类型,使用符号|表示。联合类型A|B表示,任何一个类型只要属于A或B,就属于联合类型A|B。

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

联合类型可以与值类型相结合,表示一个变量的值有若干种可能。

let setting: true | false;let gender: "male" | "female";let rainbowColor: "赤" | "橙" | "黄" | "绿" | "青" | "蓝" | "紫";

前面提到,打开编译选项strictNullChecks后,其他类型的变量不能赋值为undefined或null。这时,如果某个变量确实可能包含空值,就可以采用联合类型的写法。

let name: string | null;name = "John";
name = null;

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

直接调用会报错:

function printId(id: number | string) {console.log(id.toUpperCase()); // 报错
}

类型缩小:

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

“类型缩小”是 TypeScript处理联合类型的标准方法,凡是遇到可能为多种类型的场合,都需要先缩小类型,再进行处理。实际上,联合类型本身可以看成是一种“类型放大”(type widening),处理时就需要“类型缩小”(type narrowing)。

交叉类型

交叉类型(intersection types)指的多个类型组成的一个新类型,使用符号&表示。任何一个类型必须同时属于A和B,才属于交叉类型A&B,即交叉类型同时满足A和B的特征。

主要用途是表示对象的合成。 变量obj同时具有属性foo和属性bar。

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

常常用来为对象类型添加新属性。 类型B是一个交叉类型,用来在A的基础上增加了属性bar。

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

type 命令

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

type命令为number类型定义了一个别名Age。这样就能像使用number一样,使用Age作为类型。

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

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

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

别名支持使用表达式,也允许嵌套。

示例中,别名Greeting使用了模板字符串,读取另一个别名World。

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

typeof 运算符

JS中typeof运算符返回的都是字符串:

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

TS中返回的是TS类型:

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

同一段代码可能存在两种typeof运算符,一种用在值相关的 JavaScript 代码部分,另一种用在类型相关的 TypeScript 代码部分。如:

第一个是类型运算,第二个是值运算。

let a = 1;
let b: typeof a;if (typeof a === "number") {b = a;
}

JavaScript 的 typeof 遵守 JavaScript 规则,TypeScript 的 typeof 遵守 TypeScript 规则。它们的一个重要区别在于,编译后,前者会保留,后者会被全部删除。上例的代码编译结果如下。

let a = 1;
let b;
if (typeof a === "number") {b = a;
}

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

类型的兼容

如果类型A的值可以赋值给类型B,那么类型A就称为类型B的子类型(subtype)

如:类型number就是类型number|string的子类型。

type T = number | string;let a: number = 1;
let b: T = a;

凡是可以使用父类型的地方,都可以使用子类型,但是反过来不行。

let a: "hi" = "hi";
let b: string = "hello";b = a; // 正确
a = b; // 报错

这篇关于【TypeScript】阮一峰TypeScript教程笔记:基本用法、any 类型等、类型系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/196165

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、