TypeScript 学习记录
1. TypeScript 简介
TypeScript 是 JavaScript 的超集,它添加了静态类型系统和面向对象编程的特性,使代码更加健壮和可维护。
补充说明:
- 本文为个人的 TypeScript 学习笔记,内容基于 B 站课程视频学习,并结合豆包、扣子等ai工具查阅知识点、核对细节,融入个人思考与总结,最终通过 Trae Builder 结构化整理、多次校验后生成文档,用于个人复习与查阅参考。
- 如今学习编程的资源非常丰富,文档、视频、AI 工具都能高效辅助,大幅节省自己查阅知识点的时间,也省去注册各种知识网站账号的麻烦,特别是自学时有疑问问题或者突发奇想时,都可用ai工具回答或论证。过去我习惯零散记录、收藏他人资料,复习时难以串联知识点、难以共鸣自己的学习经历;现在借助 AI 辅助整理与校验,既能把学习过程梳理成线性、完整、严谨的笔记,又能确保内容准确、表述规范,解决了自己懒得整理的遗憾。
主要特性
- 静态类型检查
- 面向对象编程支持
- 类型推断
- 接口
- 泛型
- 装饰器
- 模块系统
安装与使用
使用 npm 安装
# 全局安装 TypeScript
npm install -g typescript
# 或当前项目安装(仅练习时更推荐)
npm install typescript --save-dev
# 验证安装成功(查看版本)
# 若全局安装
tsc -v
# 若当前项目安装,需使用 npx 执行
npx tsc -v
# 输出类似:Version 5.3.3 即为成功
编译命令
- tsc:TypeScript Compiler 命令
- -w 或 --watch:自动监听文件变化并编译
# 若全局安装
tsc -w
# 若当前项目安装,需使用 npx 执行
npx tsc -w
2. 类型系统
-
TS 的核心是编译时的类型操作:通过定义类型规则,在代码运行前就拦截类型错误,本质是给 JS 加了一层 “类型校验层”;
-
JS 的核心是运行时的变量操作:不做类型预设,直接处理变量的值,所有逻辑和错误都在运行时体现;
-
两者的本质关系:TS 是 JS 的超集,TS 的类型逻辑只存在于编译阶段,最终运行的还是 JS 的变量逻辑。
2.1 基本类型
| 类型 | 描述 | 示例 |
|---|---|---|
string | 字符串 | let name: string = "张三" |
number | 数字 | let age: number = 18 |
boolean | 布尔值 | let isActive: boolean = true |
null | 空值 | let n: null = null |
undefined | 未定义 | let u: undefined = undefined |
bigint | 大整数 | let big: bigint = 100n |
symbol | 唯一标识符 | let sym: symbol = Symbol("id") |
object | 非原始类型 | let obj: object = {} |
注意点:在JavaScript中的这些内置构造函数:Number、String、Boolean(包装对象),它们用于创建对应的包装对象,在日常开发中很少使用,在TypeScript中也是同理,所以在TypeScript中进行类型声明时,通常都是用小写的number、string、boolean (原始类型,注意:此处近关于这三种)
2.2 原始类型 vs 包装对象
- 原始类型:占用空间少,处理速度快
- 包装对象:是复杂类型,内存占用大,日常开发很少使用
- 自动装箱:JavaScript 在必要时会自动将原始类型转换为包装对象,以便调用方法或访问属性
// 作用:演示原始类型string与包装对象类型String的区别
let str1: string;
str1 = "hello";
// str1 = new String('hello') // 报错,不能将包装器对象赋值给原始类型
let str2: String;
str2 = "hello";
str2 = new String("hello"); // 通常情况下不会这么使用,故类型声明还是只用小写的string
console.log(typeof str1); // string
console.log(typeof str2); // object
### 2.3 特殊类型
| 类型 | 描述 | 示例 |
| --------- | -------------------------- | ----------------------------------------------- |
| `any` | 任意类型(放弃类型检查) | `let a: any = "hello"` |
| `unknown` | 未知类型(类型安全的 any) | `let u: unknown = 123` |
| `void` | 无返回值 | `function fn(): void {}` |
| `never` | 永远不会返回 | `function error(): never { throw new Error() }` |
| `tuple` | 固定长度和类型的数组 | `let t: [string, number] = ["hello", 123]` |
| `enum` | 枚举类型 | `enum Direction { Up, Down, Left, Right }` |
2.4 常用类型详细解释
| 类型 | 特点描述 | 使用场景 |
|---|---|---|
any | 放弃类型检查,可接受任意值,也可赋值给任意类型 | 临时绕过类型检查,迁移旧代码 |
unknown | 类型安全的 any,使用前必须进行类型检查或断言 | 不确定数据类型,但需要安全处理 |
never | 表示永远不会发生的值,不能赋值任何值 | 抛出异常的函数、无限循环、穷尽检查 |
void | 表示无返回值,函数返回空 | 无返回值的函数 |
object | 表示所有非原始类型(对象、函数、数组等) | 需要存储复杂类型,但具体类型不确定 |
tuple | 固定长度、固定类型的数组 | 函数返回多个值、精确描述数据结构 |
enum | 定义一组命名常量,增强代码可读性 | 表示一组相关的常量值 |
代码示例
any 类型
- 作用:任意类型,放弃类型检查(显式声明、隐式声明),可接受任意值,也可赋值给任意类型
let a: any = "hello";
a = 123; // 不报错
a = true; // 不报错
let str: string = a; // 不报错,但可能引发运行时错误
unknown 类型
- 作用:未知类型,类型安全的 any,可接受任意类型的值,只能赋值给 unknown 类型变量或 any 类型变量,使用前必须进行类型检查或断言
let u: unknown = "hello";
// u.toUpperCase(); // 报错:必须先进行类型检查
// 正确做法:类型检查或断言
if (typeof u === "string") {
u.toUpperCase(); // 安全
}
let str: string = u as string; // 类型断言
never 类型
- 作用:永远不会发生的值,不能赋值任何值,通常用于抛出异常或无限循环。几乎不用 never 去直接限制变量(无意义),该类型一般是 TS 主动推断出来
// 限制函数的返回值:抛出异常或进入无限循环
function throwError(msg: string): never {
throw new Error(msg);
}
void 类型
- 作用:无返回值的函数,函数返回空(void)
- 含义是【函数不返回任何值(返回值为空),调用者也不应依赖其返回值进行任何操作】,undefined 是 void 可以接受的一种"空"
- 与 undefined 的区别:void 表示无返回值,undefined 表示变量未赋值
function logMessage(msg: string): void {
console.log(msg);
}
// 与 undefined 的区别
function returnUndefined(): undefined {
return undefined; // 必须返回 undefined
}
object 类型
- 作用:小写,表示所有非原始类型(对象、函数、数组等),可接受任意对象值,也就是除了 number、string、boolean、symbol、null、undefined 以外的类型
- 无论 object 还是 Object,因范围太大,实际开发使用较少
let obj: object;
obj = {}; // OK
obj = []; // OK
obj = () => {}; // OK
// obj = "hello"; // 报错:原始类型不能赋值
// 实际开发推荐具体类型
let person: { name: string; age: number } = {
name: "张三",
age: 18
};
tuple 类型
- 作用:固定长度和类型的数组,每个元素的类型已知但可以不同。唯有元组不是关键字,用于精确描述一种特殊的数组的类型
// 定义元组类型
let person: [string, number] = ["张三", 18];
// 可选元素
let optional: [string, number?] = ["张三"]; // OK
// 函数返回多个值
function getPerson(): [string, number] {
return ["张三", 18];
}
const [name, age] = getPerson();
enum 类型
- 作用:定义一组命名常量,增强代码可读性。枚举值默认从 0 开始,也可自定义值
// 数字枚举(默认从 0 开始,也可自定义值),自动递增,且有反向映射(即可通过值来获取对应枚举成员名称)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 字符串枚举(自定义枚举值为字符串)
enum Status {
Success = "SUCCESS",
Fail = "FAIL"
}
// 使用
function move(dir: Direction) {
console.log(dir);
}
move(Direction.Up); // 传入枚举值
- 常量枚举:const 关键字定义,编译时会内联消除,避免生成额外代码(直接将枚举成员引用替换为实际值,不生成额外枚举对象)
- 内联消除默认生效条件:用 tsc 编译 + 未开启 preserveConstEnums + 仅访问常量枚举的字面量成员 → 内联消除生效,无枚举对象,仅内联值。若没有生效可检查配置文件
// 作用:常量枚举,编译时会内联消除,生成更高效的代码
const enum ConstDirection {
Up,
Down,
Left,
Right,
}
console.log(ConstDirection.Up); // 0
类型断言
告诉 TypeScript 你比它更了解类型:
let value: unknown = "hello";
let str1: string = value as string;
let str2: string = <string>value; // 断言另一种写法(不是泛型)
2.5 函数返回值为void的特殊情况
// 作用:演示void返回值的严格检查与宽松检查的区别
// 在函数定义时,限制函数返回值为void,那函数的返回值必须是空。
let LogFunc1 = function (): void {
console.log("hello");
// 除非返回undefined,返回其他值都报错
// return 6
};
// 函数类型定义和赋值分两步时:"先用type声明限制函数返回值为void时,TS并不会严格要求函数返回空",即实际赋值的函数返回不严格要求为void类型。
type LogFunc2 = () => void;
let f0: LogFunc2 = () => 666;
// 为什么:为了确保以下代码成立,Array.prototype.push返回一个数字,而Array.prototype.forEach返回类型是void
let src = [1, 2, 3];
let dst = [0];
// 简写模式。为了简写模式的成立
src.forEach((el) => dst.push(el));
// 非简写模式时:回调函数返回值为隐式undefined时,符合forEach对回调函数返回值void类型的要求
src.forEach((el) => {
dst.push(el);
});
3. 类型操作
两个用于自定义类型的方式:** ① type** 和 ** ② interface**
3.1 type
可为任意类型创建别名,让代码更简洁和可读性更强,方便类型复用和拓展。编译时起作用。编译成JS后会被移除。
- 基本用法:用type关键字定义,type后跟类型名称
- 联合类型:表示一个值可以是几种不同类型之一
- 交叉类型:允许将多个类型合并为一个类型。合并后的类型将拥有所有被合并类型的成员。交叉类型通常用于对象类型。
type gender1 = "男" | "女";
联合类型
表示一个值可以是几种不同类型之一:
type Gender = "男" | "女";
let gender: Gender = "男";
交叉类型
将多个类型合并为一个类型:
type Person = { name: string } & { age: number };
let person: Person = { name: "张三", age: 18 };
3.2 接口 interface
是一种定义结构的方式,主要作用为:类、对象、函数等规定一种锲约,这样可确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现。
3.2.1 基本语法
- 接口用于定义对象的结构,为代码提供类型约束
- 命名写法:interface IPerson{} 或者 interface PersonInterface{},通常用后者
- 接口是TS中使用最多的
// 作用:定义Person接口,包含必选属性、可选属性、只读属性和索引签名
interface Person {
name: string;
age?: number; // 可选属性
readonly id: number; // 只读属性
[key: string]: any; // 索引签名
}
let person: Person = {
name: "张三",
age: 18,
id: 1,
gender: "男", // 额外属性,通过索引签名允许
};
3.2.2 接口继承
一个interface继承另一个interface,从而实现代码复用:
// 作用:演示接口继承,Dog接口继承Animal接口并添加新方法
interface Animal {
name: string;
speak(): void;
}
interface Dog extends Animal {
bark(): void;
}
// 作用:类实现继承后的接口,必须实现所有属性和方法
class Labrador implements Dog {
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log("Woof!");
}
bark() {
console.log("Bark!");
}
}
3.2.3 接口合并
接口可以重复定义,TypeScript 会自动合并:
// 作用:演示接口自动合并,同名接口会自动合并为一个接口
interface Widget {
render(): void;
}
interface Widget {
options: { theme: string };
}
// 合并后的接口
// interface Widget {
// render(): void;
// options: { theme: string };
// }
3.2.4 何时使用接口?
- 定义对象的格式:描述数据模型、API响应格式、配置对象...等等,是开发中用的最多的场景
- 类的契约:规定一个类需要实现哪些属性和方法
- 自动合并:一般用于扩展第三方库的类型,这种特性在大型项目中可能会用到
3.2.5 interface和type的区别
- 相同点:interface和type都可以用于定义对象结构,两者在许多场景中可以互换。(interface的合并和继承,type可以用交叉类型(&)实现)
- 不同点:
- interface 更专注于定义对象和类的结构,支持继承、合并
- type 可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并
3.2.6 interface和抽象类的区别
-
相同点:都用于定义一个类的格式(应该遵循的契约)
-
不同点:
- 接口:只能描述结构,不能有任何实现代码,一个类可以实现多个接口。(义父)
- 抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类。(亲父)
-
实现要求:
- 接口和抽象类的实现 / 继承都必须满足必填结构(接口是所有成员,抽象类是仅抽象成员),且都允许添加额外属性 / 方法;
- 接口作为 "对象字面量类型" 时的 "多余属性报错" 是字面量严格检查规则,不是接口本身的限制,可通过变量赋值 / 类型断言规避;
- 抽象类的具体方法无需子类实现,只有 abstract 修饰的抽象成员才必须实现,这是和接口最核心的区别之一。
注意:表述"接口的实现不能有额外的属性或方法"是不准确的。实际上,实现接口的类完全可以有额外的方法和属性。这个限制只适用于对象字面量的严格检查(excess property checking),而不是接口实现本身的限制。
4. 类
4.1 基本语法
// 作用:定义Person类,包含属性和方法
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
speak() {
console.log(`我是${this.name},今年${this.age}岁`);
}
}
let person = new Person("张三", 18);
person.speak();
4.2 类的属性简写
// 作用:使用构造函数参数简写,自动创建并初始化类属性
class Person {
constructor(
public name: string,
protected age: number,
private id: string,
public readonly gender: string,
) {}
}
4.3 访问修饰符
| 修饰符 | 描述 |
|---|---|
public | 默认值,可在类内部、子类、类外部访问 |
protected | 可在类内部、子类访问 |
private | 仅可在类内部访问 |
readonly | 只读属性,属性无法修改 |
5. 抽象类
5.1 基本语法
抽象类是一种无法被实例化的类,专门用来定义类的结构和行为:
// 作用:抽象类定义动物的基础行为和必须实现的抽象方法
abstract class Animal {
abstract speak(): string;
breathe() {
return "Breathing...";
}
}
// 作用:具体类继承抽象类,实现抽象方法
class Dog extends Animal {
speak() {
return "Bark";
}
}
// let animal = new Animal(); // 错误:抽象类不能实例化
let dog = new Dog();
console.log(dog.speak()); // "Bark"
console.log(dog.breathe()); // "Breathing..."
5.2 何时使用抽象类
- 定义通用接口:为一组相关的类定义通用的行为(方法或属性)时
- 提供基础实现:在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现
- 确保关键实现:强制派生类实现一些关键行为
- 共享代码和逻辑:当多个类需要共享部分代码时,抽象类可以避免代码重复
5.3 注意事项
- 类继承时,重写父类的方法时建议用上override标识
6. 泛型
泛型允许在定义函数、类和接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型,泛型能让同一段代码适用于多种类型,同时保持类型的安全性。
6.1 泛型函数
泛型用在函数上:
// 作用:泛型函数,接收任意类型的数据并返回,保持类型一致性
function logData<T>(data: T): T {
console.log(data);
return data;
}
logData<number>(123);
logData<string>("hello");
// 泛型可以有多个
// 作用:多泛型参数函数,随机返回两个参数中的一个
function logData2<T, U>(data: T, data2: U): T | U {
return Math.random() > 0.5 ? data : data2;
}
6.2 泛型接口
泛型用在接口上:
// 作用:定义泛型接口,用于统一API响应格式,data字段类型可动态指定
interface Result<T> {
success: boolean;
data: T;
message: string;
}
let result: Result<number> = {
success: true,
data: 100,
message: "操作成功",
};
// 作用:泛型接口示例,gender字段类型可动态指定
interface PersonInterface3<T> {
gender: T;
}
6.3 泛型类
泛型用在类上:
// 作用:泛型容器类,可存储任意类型的数据,提供类型安全的添加和获取方法
class Container<T> {
private items: T[] = [];
add(item: T) {
this.items.push(item);
}
get(index: number): T {
return this.items[index];
}
}
let numberContainer = new Container<number>();
numberContainer.add(1);
numberContainer.add(2);
// 作用:泛型类示例,extraInfo字段类型可动态指定
class PersonO<T> {
constructor(public extraInfo: T) {}
}
new PersonO<number>(100);
7. 类型声明文件
主要作用是为现有的 JavaScript 代码提供类型信息,使得 TypeScript 能够在使用 JavaScript 库或模块时进行类型检查和提示。类型声明文件以 .d.ts 为扩展名:
7.1 基本语法
// 作用:为 JavaScript 代码提供类型声明,使 TypeScript 能够识别 JS 代码的类型
// demo.d.ts
declare function add(a: number, b: number): number;
declare const PI: number;
declare interface Person {
name: string;
age: number;
}
7.2 使用
// demo.js
function add(a, b) {
return a + b;
}
const PI = 3.14159;
// 不需要引入 .d.ts 文件,TypeScript 会自动识别(后缀不同,但名称都是 demo)
7.3 注意事项
- 使用时:.d.ts 扩展名不同于 .js,前面名称保持一致,如 demo.d.ts 和 demo.js
- 不用在 .ts 文件中引入 .d.ts 文件,TypeScript 会自动识别并使用
8. 装饰器
装饰器是一种特殊类型的声明,它可以附加到类声明、方法、访问器、属性或参数上:
8.1 类装饰器
// 作用:为类添加创建时间和获取时间的方法
function LogTime(target: Function) {
return class extends target {
createTime: Date;
constructor(...args: any[]) {
super(...args);
this.createTime = new Date();
}
getTime() {
return `该对象的创建时间是:${this.createTime}`;
}
};
}
@LogTime
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("张三");
console.log(person.getTime());
8.2 装饰器工厂
装饰器工厂是一个返回装饰器函数的函数,可以为装饰器添加参数:
// 作用:工厂函数,根据参数n为类添加重复n次的自我介绍方法
function LogIntroduceFactory(n: number) {
return function (target: Function) {
target.prototype.introduce = function () {
for (let i = 0; i < n; i++) {
console.log(`我是${this.name}`);
}
};
};
}
@LogIntroduceFactory(3)
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person("张三");
person.introduce();
8.3 属性装饰器
属性装饰器,装饰类中的属性。在类的属性前面或上面写装饰器。
属性装饰器函数参数:类中分静态属性(static 修饰,类本身的属性)和实例属性(无 static 修饰,实例对象上的属性)
target:对于静态属性来说该值是类本身,对于实例属性来说该值是类的原型对象propertyKey:属性名
// 作用:为属性添加getter/setter,实现属性拦截和私有存储
function State(target: Object, propertyKey: string) {
let key = `__${propertyKey}`;
Object.defineProperty(target, propertyKey, {
get() {
return this[key];
},
set(newVal) {
this[key] = newVal;
},
});
}
class Person {
@State age: number;
constructor(age: number) {
this.age = age;
}
}
8.4 方法装饰器
方法装饰器函数参数:
target:对于静态方法来说该值是类本身,对于实例方法来说该值是类的原型对象propertyKey:方法的名称descriptor:方法的描述对象,其中 value 属性是被装饰的方法
// 作用:为方法添加日志记录,在方法调用前后打印参数和返回值
function Logger(
target: Object,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`${propertyKey}方法调用前,参数为:${args}`);
const result = originalMethod.call(this, ...args);
console.log(`${propertyKey}方法调用后,返回值为:${result}`);
return result;
};
}
class Person {
@Logger
speak(message: string) {
console.log(`说:${message}`);
return message;
}
}
8.5 访问器装饰器
访问器装饰器:对 class 类的 get、set 方法进行装饰。class 的访问器属性 get、set 也区分实例属性和静态属性。
访问器装饰器函数参数:
target:对于静态访问器来说该值是类本身,对于实例访问器来说该值是类的原型对象propertyKey:访问器属性的名称descriptor:访问器属性的描述对象
// 作用:为setter添加范围验证,确保设置的值在指定范围内
function RangeValidate(min: number, max: number) {
return function (
target: Object,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalSet = descriptor.set;
descriptor.set = function (newVal: number) {
if (newVal < min || newVal > max) {
throw new Error(`${propertyKey}值必须在${min}到${max}之间`);
}
if (originalSet) {
originalSet.call(this, newVal);
}
};
};
}
class Weather {
private _temp: number;
@RangeValidate(-50, 50)
set temp(newTemp: number) {
this._temp = newTemp;
}
get temp() {
return this._temp;
}
}
8.6 参数装饰器
参数装饰器函数参数:
target:如果修饰的是【静态方法】的参数,该值是类本身。如果修饰的是【实例方法】的参数,该值是类的【原型对象】propertyKey:参数所在方法的名称parameterIndex:参数的索引。参数在函数参数列表中的索引,从 0 开始
// 作用:记录被装饰的参数信息(方法名和参数索引)
function Demo(target: Object, propertyKey: string, parameterIndex: number) {
console.log(`参数装饰器:方法${propertyKey}的第${parameterIndex}个参数`);
}
class Person {
speak(@Demo message: string) {
console.log(`说:${message}`);
}
}
8.7 装饰器的本质和特性
- 装饰器的本质:是函数,入参是被装饰的类或实例对象
- 类装饰器:是一个应用在类声明上的函数,可以为类添加额外的功能,或添加额外的逻辑 (注意:类装饰器在类定义时执行一次,不是在实例化时执行)
- 关于返回值:
- 类装饰器有返回值:若类返回一个新的类,那这个类将替换掉被装饰的类
- 装饰器无返回值:若类装饰器无返回值或返回undefined,那被装饰的类不会被替换
- TypeScript的设计选择:不自动推断装饰器的返回类型,由开发者显式声明
8.8 构造函数类型
在TS中,Function类型所表示的范围十分广泛,包括:普通函数、箭头函数、方法等,但并非Function类型的函数都可以被new关键字实例化,例如箭头函数是不能被实例化的,那Ts中有两种方式声明一个构造函数:
-
仅声明构造类型
type Constructor = new (...args: any[]) => any; -
声明构造类型 + 指定静态属性
type Constructor2 = { new (...args: any[]): any; // 表明该类型必须有一个构造函数 wife: string; // 表明该类型必须有静态属性:wife };
8.9 装饰器组合
- 装饰器可以组合使用,执行顺序为:先【由上到下】的执行所有的装饰器工厂,依次获取到装饰器,然后再【由下到上】的执行所有的装饰器函数
- 组合应用:可以不同装饰器函数组合使用
8.10 装饰器执行顺序
完整执行顺序(从上到下):
- 参数装饰器(Parameter Decorator)
- 方法装饰器(Method Decorator)
- 访问器装饰器(Accessor Decorator)
- 属性装饰器(Property Decorator)
- 类装饰器(Class Decorator)
执行规则:
- 参数装饰器 → 方法装饰器/访问器装饰器 → 属性装饰器 → 类装饰器
同级别装饰器的执行规则:
- 装饰器工厂执行顺序:由上到下(工厂函数的执行顺序)
- 装饰器函数执行顺序:由下到上(实际装饰器函数的执行顺序)
注意:实际执行时,对于同一个类,装饰器会按照从底层到上层的顺序执行,即先执行成员装饰器,最后执行类装饰器。
8.11 装饰器使用注意事项
- 若需要接受参数就写成返回装饰器函数的工厂函数:@装饰器(参数),不接受参数就直接写装饰器:@装饰器
- 装饰器不仅仅是覆盖一个原型上的方法,还可添加新的方法和状态等
9. 高级类型
9.1 类型守卫
// 作用:类型守卫函数,判断值是否为字符串类型,帮助TypeScript缩小类型范围
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processValue(value: unknown) {
if (isString(value)) {
// 这里 TypeScript 知道 value 是 string 类型
console.log(value.toUpperCase());
}
}
9.2 条件类型
// 作用:条件类型,根据类型参数是否为字符串返回不同的类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
9.3 映射类型
// 作用:映射类型,将接口的所有属性转换为只读属性
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
// { readonly name: string; readonly age: number; }
10. 学习资源
- 学习时参考的资源有:TypeScript 官方文档,TypeScript Book,B站视频(禹神:三小时快速上手TypeScript,TS速通教程)
11. 总结
TypeScript 是一种强大的编程语言,它通过静态类型系统和面向对象编程特性,使 JavaScript 代码更加健壮和可维护。通过学习本资料,你应该已经掌握了 TypeScript 的核心概念和使用方法,包括类型系统、接口、类、泛型、装饰器等。