建立互联网公司网站,王也诸葛青cp图,阿里云上用wordpress,seo 优化技术难度大吗深入探索 TypeScript#xff1a;从基础到高级特性
一、引言
在现代软件开发领域#xff0c;TypeScript 已经成为了一种极具影响力的编程语言。它基于 JavaScript#xff0c;并为其添加了强大的静态类型系统#xff0c;使得代码在开发阶段就能进行更严格的类型检查#x…深入探索 TypeScript从基础到高级特性
一、引言
在现代软件开发领域TypeScript 已经成为了一种极具影响力的编程语言。它基于 JavaScript并为其添加了强大的静态类型系统使得代码在开发阶段就能进行更严格的类型检查从而减少运行时错误提高代码的质量和可维护性。无论是大型企业级应用还是小型的前端项目TypeScript 都展现出了独特的优势。在这篇博客中我们将深入探讨 TypeScript 的各个方面包括它的安装、类型系统、接口、交叉类型、断言、泛型、装饰器和重载等特性让你全面了解这一强大的编程工具。
二、TypeScript 的安装与初体验
一安装 TypeScript
要开始使用 TypeScript首先需要在本地环境中安装它。使用 Node Package Managernpm可以轻松完成安装。在命令行中执行以下命令
npm install -g typescript这条命令会在全局环境中安装 TypeScript。安装完成后可以通过以下命令检查 TypeScript 的版本
tsc -v这一步确保 TypeScript 已经正确安装在你的系统中为后续的开发工作做好准备。
三、TypeScript 的类型系统
一基本类型
TypeScript 提供了丰富的基本类型包括boolean、string、number、array、null、undefined和object。这些基本类型构成了 TypeScript 类型系统的基础。
let isDone: boolean false;
let str: string hello;
let num: number 123;
let u: undefined undefined;
let n: null null;
let arr: number[] [1, 2, 3];
let strArr: string[] [a, b];
let list: any[] [1, true, hello];
let x: [string, number] [hello, 10];
let StrArr: Arraystring [a, b];在上述代码中我们可以看到不同基本类型的变量声明方式。boolean类型用于表示真假值string类型用于存储文本数据number类型用于表示数字。对于数组类型可以使用类型[]或者Array类型的方式来声明。而null和undefined在 TypeScript 中有明确的类型定义它们与其他类型的使用方式有所不同。
二元组Tuple
元组是一种特殊的数据类型它允许我们表示一个已知元素数量和类型的数组每个元素的类型可以不同。
let tupleType: [string, number] [hello, 10];在这个例子中tupleType是一个包含一个字符串和一个数字的元组。元组在处理一些具有特定结构的数据时非常有用例如函数返回多个不同类型的值时可以使用元组来接收。
三枚举Enum
枚举是一种将一组相关的命名常量组织在一起的方式它为代码增加了可读性和可维护性。
数字枚举 数字枚举默认从 0 开始依次递增。
enum Color {Red, Green, Blue};
let color: Color Color.Green;在这个例子中Color枚举定义了三个颜色常量Red的值为 0Green的值为 1Blue的值为 2。
字符串枚举 除了数字枚举还可以定义字符串枚举。
enum Color {Red red, Green green, Blue blue};
let color: Color Color.Green;字符串枚举使得每个枚举成员的值都明确指定为一个字符串这种方式在代码即文档方面有很大优势代码的可读性更强。
四特殊类型any、unknown、void 和 never
any 类型 any类型是一种可以代表任意类型的值。它允许我们绕过类型检查但过度使用any可能会导致类型系统的优势丧失。
let anyValue: any; // 任意类型 绕过 类型检查
anyValue hello;
anyValue 123;
anyValue true;
anyValue {};unknown 类型 unknown类型表示一个未知类型的值。与any不同unknown类型更加严格在使用unknown类型的值时需要先进行类型检查或断言。
let unknownValue: unknown;
unknownValue hello;
// 以下代码会报错因为 unknown 类型的值不能直接调用方法
// unknownValue.trim();
unknownValue 123;
unknownValue true;
unknownValue {};any 和 unknown 的区别 虽然any和unknown都可以绕过类型检查但它们的严格程度不同。any可以赋值给任意类型而unknown只能赋值给自身和any类型。 void 类型 void类型通常用于声明函数的返回值类型为空。
function warnUser(): void {console.log(This is a warning message);
}never 类型 never类型用于声明函数永远不会返回值例如抛出异常的函数。
function error(message: string): never {throw new Error(message);
}void和never的区别在于void表示函数没有返回值可以理解为返回undefined而never表示函数根本不会正常返回。
五Object 类型
Object类型在 TypeScript 中有特定的含义它代表标准对象即非原始类型。
// 标准对象 非原始类型
interface MyObject {create(i: object | null): any
}
// Object.prototype这里的object类型用于描述一个非原始类型的值它可以是任何对象包括通过new关键字创建的实例对象、对象字面量等。
四、接口Interface
一接口的基本概念
接口是 TypeScript 中一种重要的类型定义方式它用于对行为的抽象具体的行为则交给类来实现。接口定义了一组属性和方法的签名类必须实现这些接口中定义的内容。
interface Class {name: string;time: number;
}
let classObj: Class {name: TS,time: 2024
};在这个例子中Class接口定义了name和time两个属性classObj对象实现了这个接口它必须包含name和time属性并且类型要与接口中定义的一致。
二接口的特性
只读属性readonly和可选属性? 接口可以定义只读属性和可选属性。只读属性在初始化后不能被修改可选属性则表示该属性在实现接口时可以存在也可以不存在。
interface Point {readonly x: number; // readonly 只读属性readonly y: number;z?: number; // 可选属性
}
let p1: Point { x: 10, y: 20 };
p1.x 5; // error!面试readonly vs const const和readonly都可以用于声明常量但它们有一些区别。const声明常量后必须赋值且不能修改。
const num 10;
num 20; // error!
const obj { a: 1, b: 2 };
obj.a 3; // 这里对于对象内部属性的修改是允许的因为 const 只是保证对象的引用不变对于数组我们可以通过ReadonlyArray类型来创建只读数组。
let arr: number[] [1, 2];
let roArr: ReadonlyArraynumber arr;
roArr[0] 1; // error!
roArr.push(3); // error!
roArr.length 1; // error!
arr roArr; // error!
arr [1, 2, 3]; // ok
roArr arr; // ok可添加性 接口还可以定义任意属性使用[propName: string]: any;的方式。
interface Point {x: number;y?: number;[propName: string]: any; // 任意属性
}
let p1: Point { x: 10, z: 20, name: 坐标点位, color: red, err: 错误信息 };这种方式使得接口在处理具有动态属性的对象时更加灵活但需要注意避免滥用以免影响类型的确定性。
五、交叉类型
一交叉类型的概念
交叉类型允许我们将多个类型合并成一个类型新的类型将包含所有参与交叉的类型的属性。
interface A {a: number;
}interface B {b: string;
}type AB A B; // 交叉类型
let ab: AB { a: 1, b: hello };在这个例子中AB类型是A和B类型的交叉ab对象必须同时满足A和B接口的定义。
二更复杂的交叉类型示例
交叉类型可以用于更复杂的场景例如多个接口之间的交叉。
interface A { x: D }
interface B { x: E }
interface C { x: F }interface D { d: number }
interface E { d: string }
interface F { d: boolean }type ABC A B C;
let abc: ABC { x: { d: true, e: hello, f: 10 } };这里ABC类型是A、B和C三个接口的交叉abc对象需要满足所有相关接口对于x属性的定义虽然这个例子比较复杂但展示了交叉类型在处理复杂类型关系时的强大能力。
三type类型别名vs interface类型描述 相同点 两者都可以声明类型并且都具有一定的可拓展性。 不同点 声明次数interface可以声明多次同名的interface会自动合并。例如
interface Person {name: string;
}
interface Person {age: number;sex: string;
}而type不能声明两次如果重复声明会报错。
- **修改限制**type声明后不能再次修改它更像是一个一次性定义的类型别名。而interface在一定程度上可以通过多次声明来扩展。- **使用场景**type更偏向于多重类型的动态计算例如联合类型、交叉类型等复杂的类型操作。interface更偏向于描述对象的静态计算因为它通常用于向外暴露类型定义并且可以方便地进行扩展。四面试 2联合冲突
当使用交叉类型时可能会遇到联合冲突的情况。
interface A {a: number;b: number;
}interface B {b: string;c: boolean;
}type AB A B;
let ab: AB { a: 1, c: true };
// b: never
// 因为没有一种类型可以同时满足 A 和 B所以 b 为 never在这种情况下由于A和B对b属性的类型定义不一致且没有共同的类型满足这两个接口所以b的类型被推断为never。
六、类型断言
一类型断言的概念
类型断言是一种告诉 TypeScript 编译器我们比它更了解某个值的类型的方式它允许我们将一个值视为特定的类型从而实现类型变量的多样化。
as 语法 使用as关键字进行类型断言。
let someValue: any this is a string;
let strLength: number (someValue as string).length;尖括号语法在某些情况下可能受限 在某些环境中也可以使用尖括号的方式进行类型断言但在 React 等一些 JavaScript 框架中尖括号可能会与 JSX 语法冲突所以as语法更为常用。
let someValue: any this is a string;
let strLength: number (stringsomeValue).length;非空断言 在处理可能为undefined或null的值时可以使用非空断言。
type ClassTime () string | number;
const start (classTime: ClassTime | undefined) {let number classTime!(); // 非空断言
};二类型守卫
类型守卫是一种在代码中进行类型检查的机制它可以保证在语法规定的多种类型范围内做校验。
interface Teacher {name: string;age: number;courses: string[];
}
interface Person {name: string;age?: number;
}function getInfo(person: Person | Teacher): string {// 类型守卫 1// if ((person as Teacher).courses! undefined) {// return (person as Teacher).name 教 (person as Teacher).courses.join();// } else {// return (person as Person).name;// }// 类型守卫 2if (courses in person) {return person.name 教 (person as Teacher).courses.join();} else {return (person as Person).name;}
}在这个例子中我们通过in关键字来检查person对象是否具有courses属性从而判断person是Teacher类型还是Person类型。这种类型守卫的方式使得我们可以在处理联合类型的值时根据不同的类型执行相应的逻辑。
七、泛型
一泛型的概念
泛型是 TypeScript 中一个非常强大的特性它主要解决类、接口、方法的复用性问题以及对不特定数据类型的支持。泛型允许我们编写可以在多种类型上工作的代码而不是针对特定类型编写重复的代码。
function getDataT, U(value: T, score: U): T {return value;// 类型推断
}
let data getDatastring, number(hello, 123);在getData函数中T和U是泛型类型参数。当我们调用getData函数时可以指定T和U的具体类型这样函数就可以根据传入的类型进行相应的类型检查和操作。同时TypeScript 也具有类型推断的能力在一些情况下可以自动推断出泛型的类型。
二泛型函数的更多示例
泛型函数可以有更复杂的形式和应用场景。
function getDataT, U(value: T, score: U): String {return value;
}function getDataT, U(value: T, score: U): String {return (String(value)) as any as T;
}这些示例展示了泛型函数在处理不同类型参数和返回值类型时的灵活性但需要注意在使用类型转换时要确保类型的安全性避免出现意外的类型错误。
八、装饰器Decorator
一装饰器的概念
装饰器是一种特殊的函数它可以用来修改类、方法或属性。装饰器提供了一种简洁的方式来为代码添加额外的功能或行为。
function log(target: Function): void {// 对 target 进行加工console.log(target);target.prototype.sayHello function () {console.log(hello);}
}function nameWrapper(target: any, key: string): void {Object.defineProperty(target, key, {// setter// getter})
}
log
class Person {name: string;age: number;constructor(name: string, age: number) {nameWrapperthis.name name;this.age age;console.log(Person);console.log(this);console.log(this.name, this.age);console.log(this instanceof Person);}
}在这个例子中log装饰器函数接受一个类的构造函数作为参数并为该类添加了一个sayHello方法。nameWrapper装饰器函数则可以用于修改类的属性的描述符例如添加setter和getter方法。通过使用装饰器我们可以在不修改类的原始代码的情况下为类添加新的功能。
二装饰器的应用场景
装饰器在许多场景中都有广泛的应用比如日志记录、性能监测、权限验证等。例如在一个 Web 应用程序中可以使用装饰器来记录某个方法的调用时间或者检查用户是否有执行某个方法的权限。通过将这些功能封装在装饰器中可以使代码更加清晰和可维护将业务逻辑与这些额外的功能分离开来。