本文深入探讨了TS高级知识,涵盖了泛型、装饰器、高级类型以及模块与命名空间等内容。通过丰富的示例代码,详细讲解了这些特性在实际项目中的应用。文章旨在帮助开发者轻松掌握TypeScript高级特性和技巧,提升代码的复用性和灵活性。
TypeScript 高级知识入门教程:轻松掌握TypeScript高级特性和技巧 TypeScript 基础回顾变量声明与类型注解
在TypeScript中,变量声明时可以使用类型注解来指定变量的类型。这有助于开发人员更好地理解代码的结构和类型安全。以下是变量声明与类型注解的基本用法:
let age: number = 30;
let name: string = "Alice";
let isStudent: boolean = true;
// 数组类型注解
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["apple", "banana", "cherry"];
// 元组类型注解
let point: [number, number] = [10, 20];
// 对象类型注解
let user: { name: string; age: number } = { name: "Bob", age: 25 };
// 使用类型别名
type PointType = [number, number];
let point2: PointType = [30, 40];
函数定义与调用
在TypeScript中,定义函数时可以指定参数和返回值的类型。这有助于确保函数的类型安全,并提高代码的可读性和可维护性。以下是函数定义与调用的基本示例:
// 没有返回值的函数
function helloWorld(): void {
console.log("Hello, world!");
}
// 带返回值的函数
function add(a: number, b: number): number {
return a + b;
}
// 带参数类型注解的函数
function greet(name: string): void {
console.log(`Hello, ${name}!`);
}
// 函数作为参数
function processValue(value: number, callback: (num: number) => number): number {
return callback(value);
}
// 使用函数作为参数
let result = processValue(5, (num) => num * 2);
类与接口的基本使用
在TypeScript中,类和接口是面向对象编程的核心部分。类用于定义对象的结构和行为,而接口则用于定义对象的形状。
// 定义一个简单的类
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// 创建类的实例
let person1 = new Person("Alice", 25);
person1.greet();
// 定义接口
interface User {
name: string;
email: string;
greet(): void;
}
// 实现接口的类
class Admin implements User {
name: string;
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
greet(): void {
console.log(`Hello, my name is ${this.name} and my email is ${this.email}.`);
}
}
// 创建接口实例
let admin = new Admin("Bob", "bob@example.com");
admin.greet();
泛型的深入理解
泛型的基本概念
泛型是一种允许类型参数化的机制,这意味着在定义函数、接口或类时可以使用类型参数,而在实际使用时再指定具体的类型。泛型的主要优点是可以编写更通用且复用的代码。
// 定义一个泛型函数
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
// 使用泛型函数
let firstNumber = getFirstElement([1, 2, 3]); // 类型推断为 number
let firstString = getFirstElement(["a", "b", "c"]); // 类型推断为 string
// 定义一个泛型类
class Box<T> {
value: T;
constructor(value: T) {
this.value = value;
}
}
// 使用泛型类
let numberBox = new Box<number>(5);
let stringBox = new Box<string>("hello");
泛型的应用场景
泛型可以应用于多种场景,包括函数、类、接口等。以下是几个泛型的应用示例:
// 泛型函数
function createArray<T>(length: number, value: T): T[] {
return Array<T>(length).fill(value);
}
// 泛型类
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T {
return this.items.shift();
}
getLength(): number {
return this.items.length;
}
}
// 泛型接口
interface Map<T> {
[key: string]: T;
}
// 使用泛型接口
let map: Map<number> = { "one": 1, "two": 2 };
泛型的高级特性
泛型还支持约束、默认值、类型推断等高级特性,这些特性使得泛型更加灵活和强大。
// 泛型约束
function logValue<T extends { length: number }>(value: T): void {
console.log(`The length of the value is ${value.length}`);
}
// 使用泛型约束
logValue([1, 2, 3]);
logValue("hello"); // 不支持数字类型
// 泛型默认值
function createArray<T = number>(length: number): T[] {
return Array<T>(length).fill(null);
}
// 使用泛型默认值
let numbers = createArray(5); // 类型推断为 number[]
let strings = createArray<string>(5); // 显式指定类型为 string
装饰器的使用
装饰器的基本语法
装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression
格式,其中 expression
必须评估为一个函数,该函数将在运行时被调用并得到当前的声明作为唯一的参数。
function readonly(target: any, key: string) {
let value = target[key];
let getter = function () {
return value;
};
let setter = function (this: any, newValue: any) {
// 设置操作将被忽略
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
});
}
class Test {
@readonly
value: number = 10;
}
let test = new Test();
console.log(test.value); // 输出: 10
test.value = 20; // 设置操作将被忽略
console.log(test.value); // 输出: 10
装饰器的应用举例
装饰器在实际开发中有很多应用场景,如日志记录、权限控制、性能监控等。
function log(target: any, propertyName: string) {
console.log(`Logging ${propertyName}`);
}
class Demo {
@log
value: number = 5;
}
let demo = new Demo(); // 输出: Logging value
如何自定义装饰器
自定义装饰器可以通过函数实现,该函数接收不同的参数(根据装饰器的类型),并返回一个描述符对象或直接修改目标对象。
function readonly(target: any, key: string) {
let value = target[key];
let getter = function () {
return value;
};
let setter = function (this: any, newValue: any) {
// 设置操作将被忽略
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
});
}
class Test {
@readonly
value: number = 10;
}
let test = new Test();
console.log(test.value); // 输出: 10
test.value = 20; // 设置操作将被忽略
console.log(test.value); // 输出: 10
高级类型详解
联合类型与交集类型
联合类型和交集类型是TypeScript中处理复杂类型的重要工具。
// 联合类型
let value: number | string;
value = 10;
value = "hello"; // 类型推断为 string
// value = true; // 类型错误,值必须是 number 或 string 类型
// 交集类型
type NumberAndString = number & string;
let combined: NumberAndString; // 类型错误,number 和 string 没有交集类型
类型别名与类型保护
类型别名可以用来简化复杂的类型定义,而类型保护则用于确保在运行时值的类型。
// 类型别名
type Point = [number, number];
let point: Point = [10, 20];
// 类型保护
function isString(value: any): value is string {
return typeof value === "string";
}
function processValue(value: any) {
if (isString(value)) {
console.log(`String: ${value}`);
} else {
console.log(`Non-string: ${value}`);
}
}
processValue("hello"); // 输出: String: hello
processValue(10); // 输出: Non-string: 10
映射类型和条件类型
映射类型和条件类型是TypeScript中处理复杂类型和类型推断的重要工具。
// 映射类型
type ReadonlyKeys<T> = {
readonly [P in keyof T]: T[P];
};
let obj = { a: 1, b: 2 };
let readonlyObj: ReadonlyKeys<typeof obj> = { ...obj }; // 输出: { readonly a: 1; readonly b: 2 }
// 条件类型
type IsObject<T> = T extends object ? "is object" : "not object";
let value1: IsObject<{ a: number }>; // 类型推断为 "is object"
let value2: IsObject<string>; // 类型推断为 "not object"
模块与命名空间
模块的基本概念
模块是TypeScript中组织代码的一种方式,它允许将代码组织成模块,并通过导入导出语句来共享代码。
// 定义模块
export function add(a: number, b: number): number {
return a + b;
}
export class MathUtils {
static multiply(a: number, b: number): number {
return a * b;
}
}
// 使用模块
import { add, MathUtils } from "./mathUtils";
console.log(add(2, 3)); // 输出: 5
console.log(MathUtils.multiply(2, 3)); // 输出: 6
命名空间的作用与实现
命名空间用于组织相关的代码,避免命名冲突。命名空间中的代码可以通过模块导入导出的方式共享。
// 定义命名空间
namespace MathUtils {
export function add(a: number, b: number) {
return a + b;
}
export function subtract(a: number, b: number) {
return a - b;
}
}
// 使用命名空间
let result = MathUtils.add(2, 3); // 输出: 5
模块与命名空间的异同
- 模块:可以通过导入导出语句共享代码,但不能直接在文件全局作用域中使用。
- 命名空间:可以在文件全局作用域中使用,但需要通过导入导出语句共享代码。
// 使用模块
import { add } from "./mathUtils";
console.log(add(2, 3)); // 输出: 5
// 使用命名空间
namespace MathUtils {
export function add(a: number, b: number) {
return a + b;
}
}
console.log(MathUtils.add(2, 3)); // 输出: 5
实战演练:结合项目实践高级知识
项目中使用泛型
在实际项目中,泛型可以用于编写通用的函数和类,提高代码的复用性和灵活性。
// 定义一个通用的排序函数
function sort<T>(arr: T[], compareFn?: (a: T, b: T) => number): T[] {
return arr.sort(compareFn);
}
let numbers = [5, 3, 8, 1, 2];
let sortedNumbers = sort(numbers); // 输出: [1, 2, 3, 5, 8]
let sortedNames = sort(["Alice", "Bob", "Charlie"]); // 输出: ["Alice", "Bob", "Charlie"]
// 定义一个泛型类
class Collection<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(item: T): void {
let index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
}
}
getItems(): T[] {
return this.items;
}
}
let numberCollection = new Collection<number>();
numberCollection.add(1);
numberCollection.add(2);
numberCollection.remove(1);
console.log(numberCollection.getItems()); // 输出: [2]
let stringCollection = new Collection<string>();
stringCollection.add("hello");
stringCollection.add("world");
stringCollection.remove("hello");
console.log(stringCollection.getItems()); // 输出: ["world"]
项目中使用装饰器
在实际项目中,装饰器可以用于实现日志记录、权限控制、性能监控等功能。
// 日志记录装饰器
function log(target: any, key: string) {
let value = target[key];
let getter = function () {
console.log(`Getting ${key}`);
return value;
};
let setter = function (this: any, newValue: any) {
console.log(`Setting ${key} to ${newValue}`);
value = newValue;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
});
}
class User {
@log
name: string = "Alice";
@log
age: number = 25;
}
let user = new User();
console.log(user.name); // 输出: Getting name
console.log(user.age); // 输出: Getting age
user.name = "Bob"; // 输出: Setting name to Bob
user.age = 30; // 输出: Setting age to 30
如何在项目中应用高级类型
在实际项目中,高级类型可以用于处理复杂的数据结构和类型推断。
// 联合类型
interface User {
id: number;
name: string;
}
interface Admin extends User {
adminId: number;
}
let users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie", adminId: 10 } as Admin,
];
// 类型保护
function isUser(value: User | Admin): value is User {
return !("adminId" in value);
}
users.forEach((user) => {
if (isUser(user)) {
console.log(`User: ${user.name}`);
} else {
console.log(`Admin: ${user.name} (Admin ID: ${user.adminId})`);
}
});
// 映射类型
type ReadonlyKeys<T> = {
readonly [P in keyof T]: T[P];
};
let obj = { a: 1, b: 2 };
let readonlyObj: ReadonlyKeys<typeof obj> = { ...obj }; // 输出: { readonly a: 1; readonly b: 2 }
通过以上示例和讲解,您可以更好地理解和掌握TypeScript高级特性和技巧,并将其应用到实际项目中。希望这篇教程对您有所帮助。