【TypeScript入坑】TypeScript 的类型系统「any、unknow、never类型、数组、元组 tuple、函数、Symbol类型、对象」

news2024/9/22 7:01:10

TypeScript入坑

    • 类型注解和类型推断
    • any、unknow、never类型
      • any类型
      • unknow类型
      • never类型
    • 类型系统
      • 基本类型
      • 包装对象类型
      • Object类型与object类型
      • undefined和null的特殊性
      • 值类型
      • 联合类型
      • 交叉类型
      • type命令
      • typeof运算符
      • 类型的兼容
    • 数组
      • 简介
      • 数组的类型推断
      • 只读数组
    • 元组 tuple
      • 简介
    • 函数
      • 简介
      • Function类型
      • 可选参数
      • 参数结构
      • rest参数
      • 函数重载
    • Symbol类型
      • 简介
      • unique symbol
    • 对象
      • 简介
      • 结构类型原则
      • 严格字面量检查
      • 最小可选属性规则


类型注解和类型推断

类型注解(type annotation) :告诉 TS 变量是什么类型。例如:

// 当 TS 无法推断出变量类型的时候需要添加类型注解
function getTotal(firstNumber: number, secondNumber: number) {
  return firstNumber + secondNumber
}
const total = getTotal(1, 2)

// 其他的情况
interface Person {
  name: string
}
const rawData = '{"name": "Simon"}'
const newData: Person = JSON.parse(rawData)

// 一个变量是一个数字类型,后续要变成字符串。类似或运算符
let temp: number | string = 123
temp = '456'

类型推断(type inference):TS 会自动的去尝试分析变量的类型。例如:

// 这就是典型的类型推断,它们的类型是 number 而且值永远都不会变的
const firstnumber = 1
const secondNumber = 2
const total = firstNumber + secondNumber

any、unknow、never类型

any类型

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

let x: any;

x = 1; // 正确
x = "foo"; // 正确
x = true; // 正确

any 类型主要适用于以下两个场合:

  • 出于特殊原因,需要关闭某些变量的类型检查
  • 为了适配以前老的JavaScript项目,让代码快速迁移到TypeScript,可以把变量类型设为any

any 存在类型推断问题,如果TypeScript无法推断出类型,就会认为该变量的类型是 any

function add(x, y) {
  return x + y;
}

add(1, [1, 2, 3]); // 不报错

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

let x: any = "hello";
let y: number;

y = x; // 不报错

y * 123; // 不报错
y.toFixed(); // 不报错

unknow类型

为了解决 any 类型”污染“其他变量的问题,TypeScript 3.0引入了unknown 类型。它与any含义相同,但使用有一些限制,可以理解为严格版的any

unknown 类型赋值anyunknown 以外类型的变量都会报错,不能调用 unknown 类型变量的方法和属性,且只能进行比较运算、typeof 运算符和 instanceof 运算符。

let v: unknown = 123;
let v1: boolean = v; // 报错
let v2: number = v; // 报错

let v1: unknown = { foo: 123 };
v1.foo; // 报错
let v2: unknown = "hello";
v2.trim(); // 报错

let a: unknown = 1;
a + 1; // 报错
a === 1; // 正确

🤔 那么怎么才能使用 unknown 类型变量呢?
只有经过”类型缩小“,unknown 类型变量才可以使用。

let a: unknown = 1;

if (typeof a === "number") {
  let r = a + 10; // 正确
}

never类型

为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript 还引入了“空类型”的概念,即该类型为空,不包含任何值。
由于不存在任何属于“空类型”的值,所以该类型被称为 never,即不可能有这样的值。

类型为 never 的变量不可能给它赋任何值,否则会报错。

never 类型的变量可以赋值给任意其他类型。

function f(): never {
  throw new Error("Error");
}

let v1: number = f(); // 不报错
let v2: string = f(); // 不报错

上面函数 f() 会抛出错误,所以返回值可以写成 never,即不可能返回任何值。

🤔 为什么 never 类型可以赋值给任意其他类型呢?
这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了 never 类型。因此,never 类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。
总之,TypeScript 有两个“顶层类型”(anyunknown,但是 “底层类型”只有 never 唯一一个。


类型系统

基本类型

基础类型: boolean, number, string, void, undefined, symbol, null

包装对象类型

JavaScript中为了原始值在需要时可以执行一些方法,有”包装对象“的概念。

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

上面代码,字符串"hello"执行了 charAt() 方法,实际上是在运行时自动将原始值包装成了对象。

为了区分字面量和包装对象,TypeScript对5种原始类型分别提供了大写和小写两种类型:

  • Boolean 和 boolean
  • String 和 string
  • Number 和 number
  • BigInt 和 bigint
  • Symbol 和 symbol

建议只使用小写类型,不使用大写类型。 因为绝大部分使用原始类型的场合,都是使用字面量,不使用包装对象。而且,TypeScript 把很多内置方法的参数,定义成小写类型,使用大写类型会报错,如Math.abs()。
Symbol()和BigInt()这两个函数不能当作构造函数使用,所以没有办法直接获得 symbol 类型和 bigint 类型的包装对象,因此Symbol和BigInt这两个类型虽然存在,但是完全没有使用的理由。

Object类型与object类型

Object 类型代表了JavaScript语言里面广义对象,所有可以转成对象的值,都是 Object 类型。 事实上,除了 undefinednull 这两个值不能转为对象,其他任何值都可以赋值给 Object 类型。

object 类型代表JavaScript语言里面狭义对象,只包含对象、数组和函数。

undefined和null的特殊性

undefinednull 即是值,又是类型。

作为值,它们的特殊之处在于,任何其他类型的变量都可以赋值为 undefinednull

这样其实不利于发挥类型系统的优势,我们可以打开编译选项 strictNullChecks, 这样 undefinednull 不能赋值给其他类型的变量(除了anyunknown)。

值类型

TypeScript规定,单个值也是一种类型,称为”值类型“。

let x: "hello";

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

联合类型

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

let x: string | number;

x = 123; // 正确
x = "abc"; // 正确

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

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

交叉类型

交叉类型指的是多个类型组成的一个新类型,使用符号 & 表示。

let x: number & string;

上面实例中,变量x不可能同时是数值和字符串,所以认为x类型为 never

交叉类型的主要用途是表示对象的合成

let obj: { foo: string } & { bar: string };

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

type命令

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

type Age = number;

let age: Age = 55;

别名不允许重名,作用域为块级作用域

type Color = "red";

if (Math.random() < 0.5) {
  type Color = "blue";
}

typeof运算符

在JavaScript种,typeof 运算符只可能返回八种结果(对应8种基本类型),而且都是字符串。

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

const a = { x: 0 };

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

也就是说,同一段代码可能存在两种typeof运算符

let a = 1;
let b: typeof a; // 类型运算

if (typeof a === "number") { // 值运算
  b = a;
}

👇 上述代码编译结果如下:

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

类型的兼容

TypeScript 的类型存在兼容关系,某些类型可以兼容其他类型。

type T = number | string;

let a: number = 1;
let b: T = a;

很容易理解,上述变量a的类型是变量b的子类型,所以a可以赋值给b。


数组

简介

TypeScript 数组有一个根本特征:所有成员的类型必须相同,但是成员数量是不确定的,可以是无限数量的成员,也可以是零成员。

let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];

数组的类型推断

如果没有声明类型,初始化和赋值都会自动推断类型。

const arr = [];
arr; // 推断为 any[]

arr.push(123);
arr; // 推断类型为 number[]

arr.push("abc");
arr; // 推断类型为 (string|number)[]

只读数组

readonly 关键字声明只读数组。

const arr: readonly number[] = [0, 1];

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

也可以 as const 告诉TypeScript,推断类型时将数组推断为只读数组。

const arr = [0, 1] as const;

arr[0] = [2]; // 报错

元组 tuple

简介

元组(tuple)是 TypeScript 特有的数据类型,JavaScript 没有单独区分这种类型。它表示成员类型可以自由设置的数组,即数组的各个成员的类型可以不同。

元组必须明确声明每个成员的类型。

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

函数

简介

函数的类型声明,需要在声明函数时,给出参数的类型和返回值的类型。

function hello(txt: string): void {
  console.log("hello " + txt);
}

const repeat = (str: string, times: number): string => str.repeat(times);

Function类型

TypeScript 提供 Function 类型表示函数,任何函数都属于这个类型。

function doSomething(f: Function) {
  return f(1, 2, 3);
}

Function 类型的函数可以接受任意数量的参数,每个参数的类型都是 any,返回值的类型也是any,代表没有任何约束,所以不建议使用这个类型,给出函数详细的类型声明会更好。

可选参数

函数体内部用到可选参数时,需要判断该参数是否为 undefined

let myFunc: (a: number, b?: number) => number;

myFunc = function (x, y) {
  if (y === undefined) {
    return x;
  }
  return x + y;
};

另外注意可选参数只能定义在参数列表末尾。

参数结构

函数参数如果存在变量解构,类型写法如下。

function f([x, y]: [number, number]) {
  // ...
}

function sum({ a, b, c }: { a: number; b: number; c: number }) {
  console.log(a + b + c);
}

可以配合 type 命令一起使用,使代码更简洁。

type ABC = { a: number; b: number; c: number };

function sum({ a, b, c }: ABC) {
  console.log(a + b + c);
}

rest参数

rest 参数表示函数剩余的所有参数,它可以是数组(剩余参数类型相同),也可能是元组(剩余参数类型不同)。

// rest 参数为数组
function joinNumbers(...nums: number[]) {
  // ...
}

// rest 参数为元组
function f(...args: [boolean, number]) {
  // ...
}

函数重载

有些函数可以接受不同类型或不同个数的参数,并且根据参数的不同,会有不同的函数行为。这种根据参数类型不同,执行不同逻辑的行为,称为函数重载(function overload)。

function reverse(str: string): string;

function reverse(arr: any[]): any[];

function reverse(stringOrArray: string | any[]): string | any[] {
  if (typeof stringOrArray === "string")
    return stringOrArray.split("").reverse().join("");
  else return stringOrArray.slice().reverse();
}

Symbol类型

简介

Symbol 值通过 Symbol() 函数生成。在 TypeScript 里面,Symbol 的类型使用symbol表示。

let x: symbol = Symbol();
let y: symbol = Symbol();

x === y; // false

unique symbol

symbol 类型包含所有的Symbol值,但是无法表示某一个具体的Symbol值

为了解决这个问题,TypeScript设计了 symbol 的一个子类型 unique symbol,它表示单个的、某个具体的Symbol值。

因为其表示单个值,所以是不能修改的,只能用 const 命令声明,不能用 let 声明。

// 正确
const x: unique symbol = Symbol();
// 等同于
const x = Symbol();

// 报错
let y: unique symbol = Symbol();

简单理解,这样声明的不同变量虽然都是 unique symbol,但其实是两个值类型。

const a: unique symbol = Symbol();
const b: unique symbol = Symbol();
a === b; // 报错

const a: unique symbol = Symbol();
const b: typeof a = a; // 正确

unique symbol 类型的一个作用,就是用作属性名,这可以保证不会跟其他属性名冲突。如果要把某一个特定的 Symbol 值当作属性名,那么它的类型只能是 unique symbol,不能是 symbol

const x: unique symbol = Symbol();
const y: symbol = Symbol();

interface Foo {
  [x]: string; // 正确
  [y]: string; // 报错
}

对象

简介

// 属性类型以分号结尾
type MyObj = {
  x: number;
  y: number;
};

// 属性类型以逗号结尾
type MyObj = {
  x: number;
  y: number;
};

属性的类型可以用分号结尾,也可以用逗号结尾。

结构类型原则

只要对象 B 满足 对象 A 的结构特征,TypeScript 就认为对象 B 兼容对象 A 的类型,这称为“结构类型”原则(structual typing)。

const B = {
  x: 1,
  y: 1,
};

const A: { x: number } = B; // 正确

严格字面量检查

如果对象使用字面量表示,会触发 TypeScript 的严格字面量检查(strict object literal checking)。如果字面量的结构跟类型定义的不一样(比如多出了未定义的属性),就会报错。

const point: {
  x: number;
  y: number;
} = {
  x: 1,
  y: 1,
  z: 1, // 报错
};

如果等号右边不是字面量,而是一个变量,根据结构类型原则,不会报错。

const myPoint = {
  x: 1,
  y: 1,
  z: 1,
};

const point: {
  x: number;
  y: number;
} = myPoint; // 正确

最小可选属性规则

如果一个对象的所有属性都是可选的,会触发最小可选属性规则。

type Options = {
  a?: number;
  b?: number;
  c?: number;
};

const obj: Options = {
  d: 123, // 报错
};

上面代码,类型Options是一个对象,它的所有属性都是可选的,这导致任何对象实际都符合Options类型。所以TypeScript规定不能所有的可选属性都不存在。

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

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

相关文章

【每日刷题】Day128

【每日刷题】Day128 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 194. 二叉树的最近公…

TryHackMe 第3天 | Pre Security (中)

该学习路径讲解了网络安全入门的必备技术知识&#xff0c;比如计算机网络、网络协议、Linux命令、Windows设置等内容。上一篇中简短介绍了计算机网络相关的知识&#xff0c;本篇博客将记录 网络协议 部分。 How the web works? DNS in detail DNS (Domain name system&…

【数据结构】排序算法系列——快速排序(附源码+图解)

快速排序 接下来我们将要介绍的是排序中最为重要的算法之一——快速排序。 快速排序&#xff08;英语&#xff1a;Quicksort&#xff09;&#xff0c;又称分区交换排序&#xff08;partition-exchange sort&#xff09;&#xff0c;最早由东尼霍尔提出。快速排序通常明显比其…

XXL-JOB环境搭建

2.快速入门 2.1 下载源码 a.源码下载地址: github地址 gitee地址 2.2.环境搭建&#xff1a; a.初始化调度数据库: 1.请下载项目源码并解压&#xff0c;获取 “调度数据库初始化SQL脚本” 并执行即可 b.编译源码: 1.解压源码,按照maven格式将源码导入IDE, 使用maven进行…

【Python】使用国内镜像安装conda并创建python环境

conda介绍&#xff1a; Conda 是一个开源的包管理系统和环境管理系统&#xff0c;由 Continuum Analytics 开发。它的主要作用是简化科学计算中软件包和依赖的安装和升级&#xff0c;并允许用户轻松地在不同的环境中切换。Conda 的设计初衷是为了简化 Python 环境的搭建和管理&…

海洋大地测量基准与水下导航系列之二国外海底大地测量基准和海底观测网络发展现状(上)

海底大地控制网建设构想最先由美国斯克里普斯海洋研究所(Scripps Institution of Oceanography,SIO)提出&#xff0c;目前仅有少数发达国家具备相应技术条件。美国、日本、俄罗斯和欧盟等发达国家通过布测先进的海底大地控制网&#xff0c;不断完善海洋大地测量基准基础设施&am…

go 运行报错missing go.sum entry for module providing package

运行&#xff1a; #清理go.mod中不再需要的模块&#xff0c;并且会添加缺失的模块条目到go.sum中 go mod tidy

【全网最全】2024华为杯数学建模C题高质量成品查看论文!【附带全套代码+数据】

题 目&#xff1a; ___基于数据驱动下磁性元件的磁芯损耗建模 完整版获取&#xff1a; 点击链接加入群聊【2024华为杯数学建模助攻资料】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kxtS4vwn3gcv8oCYYyrqd0BvFc7tNfhV7&authKeyedQFZne%2BzvEfLEVg2v8FOm%…

线段树优化dp,CF 413E - Maze 2D

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 413E - Maze 2D 二、解题报告 1、思路分析 对于(li, l) -> (ri, r) …

nginx upstream转发连接错误情况研究

本次测试用到3台服务器&#xff1a; 192.168.10.115&#xff1a;转发服务器A 192.168.10.209&#xff1a;upstream下服务器1 192.168.10.210&#xff1a;upstream下服务器2 1台客户端&#xff1a;192.168.10.112 服务器A中nginx主要配置如下&#xff1a; log_format main…

接口加解密及数据加解密

目录 一、 加解密方式介绍 1.1 Hash算法加密 1.2. 对称加密 1.3 非对称加密 二、 我们要讲什么&#xff1f; 三、 接口加解密 四、 数据加解密 一、 加解密方式介绍 所有的加密方式我们可以分为三类&#xff1a;对称加密、非对称加密、Hash算法加密。 算法内部的具体实现…

Mysql高级篇(中)—— SQL优化之查询截取分析

SQL优化之查询截取分析 一、慢查询日志&#xff08;1&#xff09;简述&#xff08;2&#xff09;如何开启&#xff08;3&#xff09;慢查询日志分析工具介绍(了解)&#xff08;4&#xff09;官方工具 mysqldumpslow简述如何使用 二、SHOW PROCESSLIST三、&#xff08;了解&…

网络安全详解

目录 引言 一、网络安全概述 1.1 什么是网络安全 1.2 网络安全的重要性 二、网络安全面临的威胁 2.1 恶意软件&#xff08;Malware&#xff09; 2.2 网络钓鱼&#xff08;Phishing&#xff09; 2.3 中间人攻击&#xff08;Man-in-the-Middle Attack&#xff09; 2.4 拒…

让C#程序在linux环境运行

今晚花一些时间&#xff0c;总结net程序如何在linux环境运行的一些技术路线。 1、采用.Net Core框架 NET Core 使用了 .NET Core Runtime&#xff0c;它可以在 Windows、Linux 和 macOS 等多个操作系统上运行。可以采用Visual Studio生成Linux版本的dll。 在Linux系统中&…

救生圈检测系统源码分享

救生圈检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

Python基础学习(3)

目录 一&#xff0c;函数 1&#xff0c;函数的定义 2&#xff0c;函数的参数 1&#xff0c;默认值 2&#xff0c;传参 3&#xff0c;返回值 4&#xff0c;变量的作用域 5&#xff0c;函数的调用 二&#xff0c;常用数据结构 1&#xff0c;列表 列表的定义 列表的特性…

机器学习的应用领域

机器学习在许多领域有广泛的应用&#xff0c;下面列出了一些主要的应用领域及其典型应用&#xff1a; 1. 图像识别 人脸识别&#xff1a;用于解锁手机、自动标记照片、监控安全系统。物体识别&#xff1a;应用于自动驾驶汽车、机器人、医疗影像分析中&#xff0c;帮助机器理解…

vue3 TagInput 实现

效果 要实现类似于下面这种效果 大致原理 其实是很简单的,我们可以利用 element-plus 组件库里的 el-tag 组件来实现 这里我们可以将其抽离成一个公共的组件,那么现在有一个问题就是通讯问题 这里我们可以利用父子组件之间的通讯,利用 v-model 来实现,父组件传值,子组…

蓝桥杯15届C/C++B组省赛题目

问题描述 小蓝组织了一场算法交流会议&#xff0c;总共有 5050 人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 77 个人&#xff0c;这 77 人彼此之间没有进行握手 (但这 77 人与…

Unity数据持久化4——2进制

概述 基础知识 各类型数据转字节数据 文件操作相关 文件相关 文件流相关 文件夹相关 练习题 using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine;public class Exercises1 : MonoBehaviour {/…