TypeScript系列01-类型系统全解析
本文总结了 TypeScript 的类型系统基础,涵盖了:
- TypeScript 的价值:静态类型检查为 JavaScript 添加了类型安全保障
- 基本类型系统:从原始类型到特殊类型(
any
、unknown
、never
)的完整介绍 - 类型注解与推断:理解何时依赖自动推断,何时显式标注类型
- 类型兼容性:掌握 TypeScript 结构类型系统的核心规则
1. TypeScript 介绍
1.1 与 JavaScript 的关系
TypeScript 是 JavaScript 的超集,由 Microsoft 于 2012 年发布,设计目标是增强 JavaScript 的开发体验和代码质量。TypeScript 代码最终会被编译(转译)为原生 JavaScript,因此它可以在任何支持 JavaScript 的环境中运行。
TypeScript 与 JavaScript 的关系可以用以下方式理解:
// TypeScript = JavaScript + 静态类型系统 + 额外语言特性
1.2 静态类型检查的重要性
TypeScript 的核心价值在于引入了静态类型检查,这一特性带来了多方面的优势:
- 错误早期发现:在编译阶段识别类型错误,而非运行时才发现
- 代码智能提示:IDE 能提供更准确的代码补全和API提示
- 重构支持:安全地进行大规模代码重构
- 文档化:类型注解本身即是代码文档的一部分
- 团队协作:明确的接口定义提高了协作效率
静态类型检查通过 TypeScript 编译器(tsc)在开发阶段进行,它分析代码的类型结构并报告潜在问题,无需执行代码即可发现类型不匹配等错误。
2. 基本类型详解
2.1 原始类型:string, number, boolean
TypeScript 支持 JavaScript 的所有原始类型,并提供严格的类型检查:
// 字符串类型
let name: string = "TypeScript";
name = 42; // 错误: 不能将类型"number"分配给类型"string"// 数字类型 - 包括整数和浮点数
let age: number = 25;
let price: number = 99.99;
let infinity: number = Infinity;
let notANumber: number = NaN; // 即使是 NaN 也是 number 类型// 布尔类型
let isActive: boolean = true;
isActive = "yes"; // 错误: 不能将类型"string"分配给类型"boolean"
TypeScript 原始类型直接映射到 JavaScript 运行时的原始值,但提供了编译时的类型安全保障。
2.2 数组与元组类型
数组类型
TypeScript 中定义数组有两种语法:
// 方式 1: 类型后加方括号
let numbers: number[] = [1, 2, 3, 4, 5];// 方式 2: 使用泛型数组类型
let strings: Array<string> = ["a", "b", "c"];// 错误示例
numbers.push("six"); // 错误: 类型"string"的参数不能赋给类型"number"的参数
元组类型
元组是固定长度、元素类型可以不同的数组:
// 定义一个包含字符串和数字的元组
let person: [string, number] = ["Alice", 30];// 访问已知索引的元素,类型被正确推断
let name: string = person[0]; // 类型是 string
let age: number = person[1]; // 类型是 number// 错误示例
person[3] = "Bob"; // 错误: 索引超出元组长度
person = ["Bob", "30"]; // 错误: 类型不匹配
TypeScript 4.0 之后,元组类型支持标记和可变长度:
// 带标签的元组
type Person = [name: string, age: number];
let employee: Person = ["Bob", 42];// 可变长度元组
type StringNumberBooleans = [string, number, ...boolean[]];
let snb: StringNumberBooleans = ["hello", 42, true, false, true];
2.3 any, unknown, never 类型的区别
这三种类型代表了 TypeScript 类型系统中的特殊概念:
any 类型
any
是 TypeScript 的逃生舱,它绕过类型检查:
let flexible: any = 4;
flexible = "string now";
flexible = { complex: "object" };
flexible.nonExistentMethod(); // 不会报错!// 污染其他类型
let typedArray: number[] = [1, 2, 3];
let anyValue: any = "string";
typedArray.push(anyValue); // 不会报错,但破坏了类型安全
unknown 类型
unknown
是类型安全的 any
:
let safeValue: unknown = 4;
safeValue = "string now";
safeValue = { complex: "object" };// 错误: 对象的类型为 "unknown"
safeValue.toString(); // 正确使用方式: 先进行类型检查
if (typeof safeValue === "string") {console.log(safeValue.toUpperCase()); // 安全
}// 或使用类型断言
console.log((safeValue as string).toUpperCase());
never 类型
never
表示永远不会有值的类型:
// 返回 never 的函数不能有可达的终点
function throwError(message: string): never {throw new Error(message);
}// 无限循环也返回 never
function infiniteLoop(): never {while (true) {}
}// never 是所有类型的子类型
function controlFlow(value: string | number) {if (typeof value === "string") {// value 是 string 类型} else if (typeof value === "number") {// value 是 number 类型} else {// value 是 never 类型// 这个分支在理论上不应该被执行value; // 类型是 never}
}
2.4 void 与 null/undefined
void 类型
void
主要用于表示函数没有返回值:
// 没有返回值的函数
function logMessage(message: string): void {console.log(message);// 不需要 return 语句
}// void 类型变量只能赋值为 undefined 或 null(在 --strictNullChecks 关闭时)
let unusable: void = undefined;
unusable = null; // 仅在 --strictNullChecks 未启用时有效
null 和 undefined
这两个类型分别对应 JavaScript 中的 null
和 undefined
值:
// 明确的 null 和 undefined 类型
let n: null = null;
let u: undefined = undefined;// 在启用 --strictNullChecks 时,null 和 undefined 只能赋值给对应类型或 any/unknown
let s: string = null; // 错误: 不能将类型"null"分配给类型"string"// 使用联合类型允许 null 或 undefined
let nullable: string | null = "hello";
nullable = null; // 可以
TypeScript 的 --strictNullChecks
标志是一个重要的类型安全特性,它防止将 null
或 undefined
分配给不明确允许这些值的类型。
3. 类型注解与类型推断机制
3.1 显式类型注解的最佳实践
类型注解是 TypeScript 中显式声明变量、参数或返回值类型的方式:
// 变量类型注解
let counter: number = 0;// 函数参数和返回值类型注解
function greet(name: string): string {return `Hello, ${name}!`;
}// 对象类型注解
let user: { id: number; name: string; active?: boolean } = {id: 1,name: "Alice"
};// 函数类型注解
let callback: (data: string) => void;
callback = (data) => console.log(data);
类型注解最佳实践:
-
为公共 API 和接口添加类型注解:
// 好的做法 - 公共函数清晰标注类型 export function processData(input: string[]): ProcessedResult {// 实现... }
-
复杂或不明显的类型使用注解:
// 不明显的类型应明确注解 const result: Map<string, User[]> = groupUsersByDepartment(employees);
-
函数参数总是添加类型注解:
// 参数类型注解,返回值可以推断 function calculateTotal(items: CartItem[]) {return items.reduce((sum, item) => sum + item.price, 0); }
-
避免冗余的类型注解:
// 不好 - 冗余的类型信息 const name: string = "TypeScript";// 好 - 类型可以被推断 const name = "TypeScript";
3.2 TypeScript 类型推断的工作原理
TypeScript 的类型推断系统是其核心特性之一,它通过上下文分析推断类型:
-
变量初始化推断:
// 推断为 number 类型 let counter = 0;// 推断为 string[] 类型 const names = ["Alice", "Bob", "Charlie"];
-
函数返回值推断:
// 返回值推断为 number 类型 function add(a: number, b: number) {return a + b; }
-
上下文类型推断:
// 参数 e 被推断为 MouseEvent 类型 document.addEventListener("click", (e) => {console.log(e.clientX, e.clientY); });
-
结构化推断:
// 对象字面量推断 const user = {id: 1,name: "Alice",active: true }; // user.id 被推断为 number // user.name 被推断为 string // user.active 被推断为 boolean
类型推断机制基于 TypeScript 编译器内部的"流分析"(flow analysis)系统:
3.3 何时依赖推断,何时显式标注
选择类型推断还是显式标注的一般准则:
场景 | 推荐方式 | 原因 |
---|---|---|
变量通过字面量初始化 | 依赖推断 | 类型明显,推断准确 |
函数返回复杂类型 | 显式标注 | 清晰记录预期输出类型 |
函数参数 | 显式标注 | 提供清晰接口契约 |
类成员变量 | 显式标注 | 明确接口设计 |
空数组或对象 | 显式标注 | 推断为 any[] 或 {} |
公共 API | 显式标注 | 提供清晰的文档 |
内部实现细节 | 依赖推断 | 减少冗余,提高可维护性 |
示例:
// 基本变量初始化 - 依赖推断
const count = 42; // number
const message = "Hello"; // string
const isActive = true; // boolean// 函数参数和返回值 - 显式标注参数,可以推断简单返回值
function calculateArea(width: number, height: number) {return width * height;
}// 复杂返回类型 - 显式标注
function fetchUserData(id: string): Promise<UserProfile> {// 实现...
}// 空数组 - 显式标注
const items: CartItem[] = []; // 否则推断为 any[]// 公共 API - 显式标注
export interface UserService {findById(id: string): Promise<User>;update(user: User): Promise<void>;
}
4. 类型兼容性规则
4.1 结构类型系统详解
TypeScript 使用结构类型系统(Structural Type System),而非名义类型系统(Nominal Type System)。在结构类型系统中,类型兼容性基于类型的结构(它们包含的成员),而非它们的名称或明确的继承关系。
// 结构类型示例
interface Point {x: number;y: number;
}class Coordinate {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
}// 尽管 Coordinate 不是明确继承自 Point,但它们结构兼容
const point: Point = new Coordinate(10, 20); // 有效
在这个例子中,Coordinate
类与 Point
接口结构兼容,因为它们都有相同类型的 x
和 y
属性。
结构类型系统的主要优势:
- 灵活性:允许类型间的隐式关系,不需要明确声明继承
- 鸭子类型:如果它看起来像鸭子、叫起来像鸭子,那它就是鸭子
- 更好的 JavaScript 互操作性:贴合 JavaScript 的动态特性
4.2 兼容性判定规则与示例
对象类型兼容性
TypeScript 使用"结构子类型化"来处理对象类型的兼容性:
interface Named {name: string;
}// 多出属性的类型可以赋值给少属性的类型
let named: Named;
let person = { name: "Alice", age: 30 };
named = person; // 有效:person 有 name 属性// 但在对象字面量直接赋值时,会进行额外属性检查
named = { name: "Bob", age: 25 }; // 错误:对象字面量只能指定已知属性
对象兼容性规则:
- 源类型必须至少包含目标类型的所有必需属性
- 对应属性类型必须兼容
- 对象字面量直接赋值时有额外属性检查(可通过类型断言绕过)
函数类型兼容性
函数类型兼容性涉及参数类型和返回值类型的比较:
// 返回值类型:源函数的返回类型必须可分配给目标函数的返回类型
type Logger = (message: string) => void;
type StringTransformer = (message: string) => string;let loggerFunc: Logger;
let transformerFunc: StringTransformer = (message) => message.toUpperCase();// 有效:string 可以分配给 void
loggerFunc = transformerFunc; // 参数类型:目标函数的参数必须可分配给源函数的参数
type MouseHandler = (event: MouseEvent) => void;
type EventHandler = (event: Event) => void;let mouseHandler: MouseHandler;
let eventHandler: EventHandler = (e) => console.log(e.type);// 有效:MouseEvent 是 Event 的子类型
mouseHandler = eventHandler;// 但反过来不行
eventHandler = mouseHandler; // 错误:MouseEvent 有 Event 没有的属性
函数兼容性规则:
- 返回类型:源函数返回类型必须可分配给目标函数返回类型
- 参数数量:源函数可以有更少的参数(但不能有更多)
- 参数类型:源函数参数类型必须"更宽松"(逆变位置)
泛型类型兼容性
泛型类型的兼容性取决于泛型参数的使用方式:
// 泛型参数未使用,兼容性不受泛型影响
interface Container<T> {tag: string;
}let numberContainer: Container<number> = { tag: "numbers" };
let stringContainer: Container<string> = { tag: "strings" };// 兼容,因为 T 未在结构中使用
numberContainer = stringContainer;// 泛型参数被使用,兼容性受泛型影响
interface ValueContainer<T> {value: T;
}let numValue: ValueContainer<number> = { value: 123 };
let strValue: ValueContainer<string> = { value: "hello" };// 不兼容,因为 T 在结构中使用
numValue = strValue; // 错误
类兼容性
类与接口类似,但有两个不同点:
class Animal {protected name: string;constructor(name: string) { this.name = name; }
}class Dog extends Animal {breed: string;constructor(name: string, breed: string) {super(name);this.breed = breed;}
}let animal: Animal;
let dog: Dog;// private 和 protected 成员会影响兼容性
animal = dog; // 有效
dog = animal; // 错误:Animal 没有 breed 属性class Person {protected name: string;constructor(name: string) { this.name = name; }
}// 即使结构相同,不同类中的 protected 成员也被视为不同
animal = new Person("human"); // 错误
类兼容性规则:
- 只比较实例成员(静态成员和构造函数不影响兼容性)
- private 和 protected 成员必须来自同一个类定义才兼容
总结
TypeScript 类型系统强大而灵活,它不仅能捕获常见错误,还能作为代码文档,提升开发效率和代码质量。
相关文章:

TypeScript系列01-类型系统全解析
本文总结了 TypeScript 的类型系统基础,涵盖了: TypeScript 的价值:静态类型检查为 JavaScript 添加了类型安全保障基本类型系统:从原始类型到特殊类型(any、unknown、never)的完整介绍类型注解与推断&…...
ragflow-mysql 启动失败案例分析
一、问题描述 1.拉取RAGflow镜像失败 dependency failed to start: container ragflow-mysql is unhealthy2. 查询日志 docker logs ragflow-mysql显示 出现[rootlocalhost docker]# docker logs ragflow-mysql Fatal glibc error: CPU does not support x86-64-v2 Fatal …...
SslConnection::SslConnection()详解
一、🔍 SslConnection::SslConnection() 详解 这个构造函数的主要作用是: 创建 SSL 对象创建 BIO(I/O 缓冲区)初始化 SSL 服务器模式绑定回调函数(onRead() 处理接收数据) 📌 1. 初始化 SSL 相…...

unity lua属性绑定刷新
我们现在有一个 角色属性类叫heroModel,内容如下,当heroModel中的等级发生变化的时候,我们需要刷新界面显示等级信息,通常我们是在收到等级升级成功的协议的时候,发送一个事件,UI界面接受到这个事件的时候,刷新一下等级…...

Self-Pro: A Self-Prompt and Tuning Framework for Graph Neural Networks
Self-Pro: A Self-Prompt and Tuning Framework for Graph Neural Networks #paper/GFM/GNN-BASED# #paper/⭐⭐⭐# 注意:这篇文章是每个图一个GCN模型,而不是所有图一个GCN 模型 算是最早的涉及异配图的prompt了 贡献和动机: 非对…...
企业级-数据分类分级详细方案
一、方案背景 在数字化时代,数据成为企业和组织的核心资产。随着数据量的快速增长和数据应用场景的不断拓展,如何有效地管理和保护数据,确保数据的安全性、合规性和可用性,成为了亟待解决的问题。数据分类分级作为数据管理的基础工作,能够帮助企业清晰地了解自身的数据资…...

本地部署Qwen2.5-VL-7B-Instruct模型
本地部署Qwen2.5-VL-7B-Instruct模型 本地部署Permalink **创建环境** conda create -n qwenvl python3.11 -y# 报错: Solving environment: failedPackagesNotFoundError: The following packages are not available from current channels:# 处理: c…...
【前端】简单原生实例合集html,css,js
长期补充,建议关注收藏点赞。 目录 a标签设置不一样的花样(图片但不用img)侧边固定box分栏input各种类型iframe表单拖拽 a标签设置不一样的花样(图片但不用img) a标签里面不用嵌套img,直接设置为其bg-img即可 <!DOCTYPE html…...
【Spring】配置文件的使用
在Spring框架中,application.properties(或application.yml)文件用于配置Spring应用程序的各种属性。我们可以通过多种方式来使用这些配置,包括使用Value和ConfigurationProperties注解来绑定配置到Java对象。 下面是对不同配置类…...

MOM成功实施分享(七)电力电容制造MOM工艺分析与解决方案(第一部分)
声明:文章仅用于交流学习,不用于商业项目实施,图片来源于网络,如有侵犯权利,请联系作者及时删除。 本方案旨在对电力电容(PEC和PQM型号)制造工艺深度分析,结合管理要求设计MOM相关功…...

计算机毕业设计SpringBoot+Vue.js航空机票预定系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

Python 爬取唐诗宋词三百首
你可以使用 requests 和 BeautifulSoup 来爬取《唐诗三百首》和《宋词三百首》的数据。以下是一个基本的 Python 爬虫示例,它从 中华诗词网 或类似的网站获取数据并保存为 JSON 文件。 import requests from bs4 import BeautifulSoup import json import time# 爬取…...

【二.提示词工程与实战应用篇】【3.Prompt调优:让AI更懂你的需求】
最近老张在朋友圈秀出用AI生成的国风水墨画,隔壁王姐用AI写了份惊艳全场的年终总结,就连楼下小卖部老板都在用AI生成营销文案。你看着自己跟AI对话时满屏的"我不太明白您的意思",是不是怀疑自己买了台假电脑?别慌,这可能是你的打开方式不对。今天咱们就聊聊这个…...
商城源码的框架
商城源码的框架通常是基于某种Web开发框架或者电子商务平台来构建的。以下是一些常见的商城源码框架: WooCommerce:基于WordPress的电子商务插件,适用于小型到中型的在线商店。 Magento:一个功能强大和灵活的开源电子商务平台&am…...

WordPress如何防Webshell、防篡改、防劫持,提升WP漏洞防护能力
WordPress是一款世界知名的CMS系统,不仅可以创建博客网站,还可以用于建设企业网站、下载网站、商城等各类网站。功能非常强大、结构科学合理,深受广大用户喜欢。 虽然WordPress非常优秀,但是为了保障网站安全,我们还是…...

Android Flow 示例
在Android开发的世界里,处理异步数据流一直是一个挑战。随着Kotlin的流行,Flow作为Kotlin协程库的一部分,为开发者提供了一种全新的方式来处理这些问题。今天,我将深入探讨Flow的设计理念,并通过具体的例子展示如何在实…...
刚安装docker并启动docker服务: systemctl restart docker报错解决
root:/home/lzw# sudo systemctl restart docker Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xeu docker.service" for details. 1、问题描述 启动doc…...
xss笔记与打靶(更新中)
这个文章好 https://blog.csdn.net/huangyongkang666/article/details/123624164?fromshareblogdetail&sharetypeblogdetail&sharerId123624164&sharereferPC&sharesource2401_88818565&sharefromfrom_link 什么是xss XSS(跨站脚本攻击&…...

游戏引擎学习第133天
仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾并设定今天的主题 今天的任务是进一步优化背景资源的流式加载,尤其是在内存管理方面。昨天,我们实现了资源流式加载,让游戏在加载时可以动态地加载背景,而不是一开始就把所有资…...

【鸿蒙操作系统】- 1:实习阶段的一些总结
本文目录 1. 序2.鸿蒙与欧拉的概念微内核LiteOS鸿蒙微内核POSIX标准 3.实习干了些什么身份鉴别访问控制恶意代码防范安全审计入侵防范性能压测检查系统版本网络测试常见的linux测试命令 1. 序 之前在某国企实习的时候,有幸参与了鸿蒙系统、鸿蒙欧拉的项目ÿ…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...