【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; // 报错

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

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

相关文章

web基础以及http协议

web基础,http协议 域名:www.88886.co DNS解析 静态页面 动态页面 DNS域名: 网络上的通信都是基于IP通信模式:TCP/IP TCP建立连接和断开连接,都是要双方进行确认的 建立连接:三次握手 断开连接&#x…

Spring源码解析(十二):TransactionInterceptor事务拦截器

Spring源码系列文章 Spring源码解析(一):环境搭建 Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三):bean容器的刷新 Spring源码解析(四):单例bean的创建流程 Spring源码解析(五)&…

dockerfile lnmp 搭建wordpress、docker-compose搭建wordpress

-----------------安装 Docker--------------------------- 目前 Docker 只能支持 64 位系统。systemctl stop firewalld.service setenforce 0#安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 --------------------------------------------------…

NoSQL之Redis 主从复制配置详解及哨兵模式

目录 1 Redis 主从复制 1.1 主从复制的作用 1.2 主从复制流程 2 搭建Redis 主从复制 2.1 安装 Redis 2.2 修改 Redis 配置文件(Master节点操作) 2.3 修改 Redis 配置文件(Slave节点操作) 2.4 验证主从效果 3 Redis 哨兵模…

揭秘 Go 中的 new() 和 make() 函数

Go(或 Golang)是一种现代、静态类型、编译型的编程语言,专为构建可扩展、并发和高效的软件而设计。它提供了各种内置的函数和特性,帮助开发人员编写简洁高效的代码。其中包括 new() 和 make() 函数,这两个函数乍看起来…

一个命令让redis服务端所有信息无所遁形~(收藏吃灰系列)

Redis服务器是一个事件驱动程序,它主要处理两类事件:文件事件和时间事件。这些事件的处理和Redis命令的执行密切相关。下面我将以Redis服务端命令为切入点,深入解析其工作原理和重要性。 首先,我们先了解Redis服务端有哪些命令。…

【JavaScript】浅拷贝与深拷贝

引言 浅拷贝、深拷贝是对引用类型而言的。 引用类型的变量对应一个栈区地址,这个栈区地址处存储的值是存放的真正的数据的堆区地址。 基本数据类型的变量也对应一个栈区地址,但是该地址存储的是其真正的值。 let a b发生了什么? let obj…

this关键字在不同上下文中的值是如何确定的?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

vsftpd使用

安装vsftpd yum install vsftpd -y版本:3.0.2三种访问方式:匿名访问、本地用户访问、虚拟用户访问 虚拟用户访问配置 # 1. 配置虚拟用户 cd /etc/vsftpd mv vsftpd.conf vsftpd.conf.bak grep -v "#" vsftpd.conf.bak > vsftpd.conf ech…

利用互斥锁实现多个线程写一个文件

代码 #include <stdio.h> #include <pthread.h> #include <string.h> #include <unistd.h>FILE *fp;//线程函数1 void *wrfunc1(void *arg); //线程函数2 void *wrfunc2(void *arg); //线程函数3 void *wrfunc3(void *arg);//静态创建互斥锁 pthread_…

Python爬虫技术系列-03requests库案例-完善

Python爬虫技术系列-03requests库案例 参考1 Requests基本使用1.1 Requests库安装与使用1.1.1 Requests库安装1.1.2 Rrequests库介绍1.1.3 使用Requests一般分为三个步骤1.1.4 requests的公共方法 2 Requests库使用案例2.1 GET请求携带参数和headers2.2 POST请求&#xff0c;写…

(8)SpringMVC中的视图类型及其特点,以及视图控制器view-controller的配置

SpringMVC的视图 转发和重定向的区别及其原理,参考文章 视图类型及特点 视图的作用就是将Model中的数据渲染到页面上并展示给用户,SpringMVC中视图对应的View接口有三种实现类对应三种视图解析器 默认有转发视图InternalResourceView和重定向视图RedirectView以及Thymeleaf…

edu 156 div2 c

#include <bits/stdc.h> using namespace std; using ll long long; void solve(){string s;cin>>s;//s s;ll t;cin>>t;//t--;ll pos,k;ll len 0;for(int i 0 ; i < s.length() ; i){if(len s.length() - i > t){pos t - len;//第几个字符k i;…

【接口干货】热门、免费api集合

手机号码归属地&#xff1a;可根据手机号码查询其省市区、运营商区号行政区划代码等信息。 上亿条数据囊括最新的170、166、147等号段&#xff0c;更新及时、准确度高。 短信验证码&#xff1a;可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商&#xff0c;…

Cpolar内网穿透工具在windows和Linux上具体使用

Cpolar内网穿透工具在windows和Linux上具体使用 一、Linux上部署的项目通过内网穿透实现外网访问项目二、Windows上部署的项目通过内网穿透实现外网访问项目 一、Linux上部署的项目通过内网穿透实现外网访问项目 一个免费的内网穿透方式&#xff0c;简单方便。 网址&#xff1a…

CCF中国开源大会专访|毛晓光:“联合”是开源走向“共赢”的必由之路

受访嘉宾 | 毛晓光 记者 | 朱珂欣 2023 CCF 中国开源大会&#xff08; CCF ChinaOSC &#xff09;拟于 2023 年 10 月 21 日至 22 日在湖南省长沙市北辰国际会议中心召开。 作为第二届 CCF 中国开源大会&#xff0c;本届大会将组织特邀报告、高峰论坛和领域分论坛等不同类…

智能电表怎么远程读数?

随着科技的飞速发展&#xff0c;智能电表已经成为了家庭用电管理的重要工具。相比传统的电表&#xff0c;智能电表具有远程读数、自动抄表、用电分析等功能&#xff0c;让家庭用电更加便捷、智能。那么&#xff0c;智能电表是如何实现远程读数的呢?下面小编来为大家讲解一下智…

JVM 运行时数据区和垃圾收集算法

在 《深入理解 Java 虚拟机》一书中&#xff0c;作者将运行时数据区和垃圾收集算法放在开头章节&#xff0c;说明了这两个知识点是进一步学习 JVM 的基础知识点&#xff0c;相比后续的 垃圾收集器和 JMM&#xff0c;它也更加的简单。 运行时数据区 运行时数据区是《Java 虚拟…

Centos7使用nginx搭建rtmp流媒体服务器

为什么写这篇文章 2023年10月份&#xff0c;公司系统中有个需求&#xff0c;需要使用摄像头记录工程师在维修设备时的工作状态&#xff0c;找到了一家做执法记录仪的厂商&#xff0c;通过厂商发过来的文档了解到该执法记录仪支持通过rtmp协议推流至服务器&#xff0c;第一次接…

【SQL】MySQL中的存储引擎、事务、锁、日志

存储引擎&#xff1a; 数据库管理系统&#xff08;DBMS&#xff09;使用数据存储引擎进行创建、查询、更新和删除数据。 MySQL5.5之前默认的存储引擎是MyISAM&#xff0c;5.5及之后版本默认的存储引擎是InnoDB。(my.ini中指定的) MyISAM&#xff1a;不支持事务&#xff0c;不支…