Bootstrap

2024-11-25TypeScript高阶知识

2-TypeScript高阶知识

1-TypeScipt中 装饰器和元数据 的代码示例


在 TypeScript 中,装饰器(Decorators)和元数据(Metadata)是两个强大的功能,它们可以帮助你以声明式的方式扩展类的行为和属性。下面是一些代码示例,展示了如何在 TypeScript 中使用装饰器和元数据。

1. 装饰器(Decorators)

装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression 这种形式,其中 expression 必须是一个函数,它会在运行时被调用,并带有关于被装饰的声明的信息。

类装饰器示例
// 定义一个类装饰器
function LogClass(target: Function) {
    console.log(`Class ${target.name} is being decorated.`);
}

// 使用类装饰器
@LogClass
class MyClass {
    constructor() {
        console.log('MyClass instance created.');
    }
}

// 创建类的实例
const instance = new MyClass();
方法装饰器示例
// 定义一个方法装饰器
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
        console.log(`Method ${propertyKey} is called with arguments:`, args);
        return originalMethod.apply(this, args);
    };

    return descriptor;
}

// 使用方法装饰器
class MyClass {
    @LogMethod
    greet(name: string) {
        console.log(`Hello, ${name}!`);
    }
}

// 调用方法
const instance = new MyClass();
instance.greet('Alice');

2. 元数据(Metadata)

元数据可以在运行时将额外的信息与类的装饰器一起存储。你可以使用 reflect-metadata 库来访问和操作这些元数据。

安装 reflect-metadata

首先,你需要安装 reflect-metadata 库:

npm install reflect-metadata

然后在你的 tsconfig.json 文件中启用 emitDecoratorMetadata 选项:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
使用元数据示例
import 'reflect-metadata';

// 定义一个属性装饰器
function LogProperty(target: any, propertyKey: string) {
    const type = Reflect.getMetadata('design:type', target, propertyKey);
    console.log(`Property ${propertyKey} has type:`, type.name);
}

// 使用属性装饰器
class MyClass {
    @LogProperty
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

// 创建类的实例
const instance = new MyClass('Alice');

在这个例子中,@LogProperty 装饰器会记录 name 属性的类型信息。

总结

TypeScript 的装饰器和元数据功能为开发者提供了一种强大且灵活的方式来扩展和修改类的行为。你可以通过装饰器来实现各种高级功能,如日志记录、权限控制、依赖注入等,而元数据则可以帮助你在运行时获取更多关于类的信息。

2-TypeScipt中 模块和命名空间 的代码示例


在 TypeScript 中,模块(Modules)和命名空间(Namespaces)是两种组织代码的方式。模块用于将代码分割成独立的文件,而命名空间则用于在同一个文件中组织代码。下面是一些代码示例,展示了如何在 TypeScript 中使用模块和命名空间。

1. 模块(Modules)

模块是 TypeScript 中组织代码的主要方式。每个模块都包含一个独立的文件,模块之间的代码不会相互污染。模块可以通过 exportimport 关键字进行导出和导入。

示例:导出和导入模块
// math.ts
export function add(a: number, b: number): number {
    return a + b;
}

export function subtract(a: number, b: number): number {
    return a - b;
}

// main.ts
import { add, subtract } from './math';

console.log(add(5, 3));        // 输出: 8
console.log(subtract(5, 3));   // 输出: 2
示例:默认导出

在模块中,你可以使用 export default 导出一个默认的模块成员。

// greet.ts
export default function greet(name: string): void {
    console.log(`Hello, ${name}!`);
}

// main.ts
import greet from './greet';

greet('Alice');  // 输出: Hello, Alice!

2. 命名空间(Namespaces)

命名空间用于在同一个文件中组织代码,避免全局作用域的污染。命名空间使用 namespace 关键字定义,并且可以通过点语法来访问命名空间中的成员。

示例:定义和使用命名空间
// 定义一个命名空间
namespace MathUtils {
    export function add(a: number, b: number): number {
        return a + b;
    }

    export function subtract(a: number, b: number): number {
        return a - b;
    }
}

// 使用命名空间中的函数
console.log(MathUtils.add(5, 3));        // 输出: 8
console.log(MathUtils.subtract(5, 3));   // 输出: 2
示例:嵌套命名空间

命名空间可以嵌套,形成更加复杂的组织结构。

namespace Outer {
    export namespace Inner {
        export function greet(name: string): void {
            console.log(`Hello, ${name}!`);
        }
    }
}

// 使用嵌套的命名空间
Outer.Inner.greet('Alice');  // 输出: Hello, Alice!

3. 混合使用模块和命名空间

你可以在模块中使用命名空间,也可以在命名空间中使用模块。这种方式可以帮助你更好地组织代码。

示例:在模块中使用命名空间
// mathUtils.ts
namespace MathUtils {
    export function add(a: number, b: number): number {
        return a + b;
    }

    export function subtract(a: number, b: number): number {
        return a - b;
    }
}

export default MathUtils;

// main.ts
import MathUtils from './mathUtils';

console.log(MathUtils.add(5, 3));        // 输出: 8
console.log(MathUtils.subtract(5, 3));   // 输出: 2

总结

TypeScript 中的模块和命名空间提供了不同的方式来组织代码。模块适用于将代码分割成独立的文件,并通过 importexport 关键字进行导入和导出。而命名空间则适用于在同一个文件中组织代码,避免全局作用域的污染。你可以根据项目的需要选择合适的方式来组织代码。

3-TypeScipt中 异步编程-Promise 的代码示例


在 TypeScript 中,异步编程是非常常见的任务。Promise 是一种处理异步操作的方式,它表示一个可能在未来某个时间点完成的操作(成功或失败)。下面是一些 TypeScript 中使用 Promise 进行异步编程的代码示例。

1. 基本 Promise 示例

下面的示例展示了如何使用 Promise 来处理一个简单的异步操作。

示例:创建和使用 Promise
// 定义一个简单的异步函数
function delay(ms: number): Promise<void> {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

// 使用 Promise
async function main() {
    console.log('Start');
    await delay(2000);  // 等待2秒钟
    console.log('End');
}

main();

在这个例子中,delay 函数返回一个 Promise,它会在指定的毫秒数后完成。main 函数使用 await 关键字来等待 delay 完成,然后再继续执行。

2. 处理成功和失败

Promise 可以处理成功和失败的情况。你可以使用 .then().catch() 方法来分别处理成功和失败的情况。

示例:处理 Promise 的成功和失败
// 定义一个可能会失败的异步函数
function fetchData(): Promise<string> {
    return new Promise((resolve, reject) => {
        const success = Math.random() > 0.5;  // 随机决定成功或失败

        if (success) {
            resolve('Data fetched successfully');
        } else {
            reject('Failed to fetch data');
        }
    });
}

// 使用 Promise
fetchData()
    .then((data) => {
        console.log(data);
    })
    .catch((error) => {
        console.error(error);
    });

在这个例子中,fetchData 函数返回一个 Promise,它有 50% 的概率成功,50% 的概率失败。我们使用 .then() 来处理成功的情况,使用 .catch() 来处理失败的情况。

3. 多个 Promise 的处理

有时你需要处理多个 Promise。TypeScript 提供了 Promise.allPromise.race 方法来处理多个 Promise。

示例:使用 Promise.all

Promise.all 方法会等待所有的 Promise 都成功完成,并将它们的结果作为一个数组返回。如果有任何一个 Promise 失败,Promise.all 会立即返回该失败的结果。

// 定义两个异步函数
function delayAndLog(ms: number, message: string): Promise<string> {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(message);
            resolve(message);
        }, ms);
    });
}

// 使用 Promise.all
async function main() {
    const promises = [
        delayAndLog(1000, 'First'),
        delayAndLog(2000, 'Second'),
        delayAndLog(3000, 'Third')
    ];

    const results = await Promise.all(promises);
    console.log('All promises resolved:', results);
}

main();
示例:使用 Promise.race

Promise.race 方法会等待第一个完成的 Promise,并返回它的结果(无论成功还是失败)。

// 使用 Promise.race
async function main() {
    const promises = [
        delayAndLog(1000, 'First'),
        delayAndLog(2000, 'Second'),
        delayAndLog(3000, 'Third')
    ];

    const result = await Promise.race(promises);
    console.log('First promise resolved:', result);
}

main();

总结

Promise 是 TypeScript 中处理异步操作的一种强大工具。你可以使用 new Promise 构造函数来创建 Promise,并使用 .then().catch() 方法来处理成功和失败的情况。对于多个 Promise,你可以使用 Promise.allPromise.race 来处理。通过这些工具,你可以更有效地管理异步代码的执行流程。

4-TypeScipt中 异步编程-async/wait 的代码示例


在 TypeScript 中,async/await 是处理异步操作的一种更简洁和直观的方式。async/await 建立在 Promise 的基础上,使得异步代码看起来更像同步代码。下面是一些使用 async/await 进行异步编程的代码示例。

1. 基本 async/await 示例

下面的示例展示了如何使用 async/await 来处理异步操作。

示例:使用 async/await 等待 Promise 完成
// 定义一个简单的异步函数
function delay(ms: number): Promise<void> {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

// 使用 async/await
async function main() {
    console.log('Start');
    await delay(2000);  // 等待2秒钟
    console.log('End');
}

main();

在这个例子中,delay 函数返回一个 Promise,它会在指定的毫秒数后完成。main 函数使用 await 关键字来等待 delay 完成,然后再继续执行。

2. 处理成功和失败

await 关键字可以捕获 Promise 的成功和失败。你可以使用 try/catch 块来处理异步操作中的错误。

示例:使用 try/catch 处理异步错误
// 定义一个可能会失败的异步函数
function fetchData(): Promise<string> {
    return new Promise((resolve, reject) => {
        const success = Math.random() > 0.5;  // 随机决定成功或失败

        if (success) {
            resolve('Data fetched successfully');
        } else {
            reject('Failed to fetch data');
        }
    });
}

// 使用 async/await 和 try/catch
async function main() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}

main();

在这个例子中,fetchData 函数返回一个 Promise,它有 50% 的概率成功,50% 的概率失败。我们在 try 块中使用 await 来等待 fetchData 的结果,如果成功,则输出数据;如果失败,则捕获错误并在 catch 块中处理。

3. 多个异步操作的顺序执行

你可以使用多个 await 关键字来按顺序执行多个异步操作。

示例:按顺序执行多个异步操作
// 定义两个异步函数
function delayAndLog(ms: number, message: string): Promise<string> {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(message);
            resolve(message);
        }, ms);
    });
}

// 使用 async/await 按顺序执行多个异步操作
async function main() {
    console.log('Start');
    await delayAndLog(1000, 'First');
    await delayAndLog(2000, 'Second');
    await delayAndLog(3000, 'Third');
    console.log('End');
}

main();

在这个例子中,delayAndLog 函数会在指定的毫秒数后输出消息,并返回该消息。main 函数按顺序等待每个异步操作完成,然后再继续执行下一个操作。

4. 并发执行多个异步操作

如果你想并发执行多个异步操作,并等待它们全部完成,可以使用 Promise.all

示例:并发执行多个异步操作
// 使用 async/await 并发执行多个异步操作
async function main() {
    console.log('Start');
    const promises = [
        delayAndLog(1000, 'First'),
        delayAndLog(2000, 'Second'),
        delayAndLog(3000, 'Third')
    ];

    const results = await Promise.all(promises);
    console.log('All promises resolved:', results);
    console.log('End');
}

main();

在这个例子中,Promise.all 会并发执行所有的 delayAndLog 操作,并等待它们全部完成。然后,await 关键字会返回一个包含所有结果的数组。

总结

async/await 是 TypeScript 中处理异步操作的一种强大且简洁的方式。通过 async 函数和 await 关键字,你可以像编写同步代码一样编写异步代码,并通过 try/catch 块来处理错误。对于多个异步操作,你可以按顺序执行,也可以使用 Promise.all 并发执行并等待结果。

5-TypeScipt中 异步编程-生成器函数 的代码示例


在 TypeScript 中,生成器函数(Generator Functions)是一种特殊的函数,它可以通过 yield 关键字暂停执行,并在需要时恢复执行。生成器函数通常用于处理异步操作,特别是在需要逐步处理数据流时非常有用。

基本生成器函数示例

下面的示例展示了如何在 TypeScript 中定义和使用生成器函数。

示例:基本生成器函数
// 定义一个生成器函数
function* simpleGenerator(): Generator<number, void, void> {
    let count = 0;
    while (true) {
        yield count++;
    }
}

// 使用生成器函数
const gen = simpleGenerator();

console.log(gen.next().value);  // 输出: 0
console.log(gen.next().value);  // 输出: 1
console.log(gen.next().value);  // 输出: 2

在这个例子中,simpleGenerator 是一个生成器函数,它使用 yield 关键字来逐步生成数值。每次调用 gen.next() 时,生成器函数会从上次暂停的地方继续执行,并返回下一个 yield 的值。

异步生成器函数示例

生成器函数可以与异步操作结合使用,特别是在需要逐步处理异步数据流时。

示例:异步生成器函数
// 定义一个异步延迟函数
function delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

// 定义一个异步生成器函数
async function* asyncGenerator(): AsyncGenerator<string, void, void> {
    let count = 0;
    while (count < 3) {
        await delay(1000);  // 等待1秒钟
        yield `Data ${count++}`;
    }
}

// 使用异步生成器函数
(async () => {
    const gen = asyncGenerator();
    for await (const value of gen) {
        console.log(value);
    }
})();

在这个例子中,asyncGenerator 是一个异步生成器函数,它使用 await 关键字来等待异步操作(这里是 delay 函数),并使用 yield 关键字逐步生成数据。我们通过 for await...of 语法来迭代异步生成器,逐步处理生成的数据。

异步生成器函数与 Promise 结合示例

你还可以将异步生成器函数与 Promise 结合起来,处理更复杂的异步操作。

示例:异步生成器函数与 Promise 结合
// 定义一个异步延迟函数
function delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

// 定义一个异步生成器函数
async function* asyncGenerator(): AsyncGenerator<string, void, void> {
    let count = 0;
    while (count < 3) {
        await delay(1000);  // 等待1秒钟
        yield `Data ${count++}`;
    }
}

// 使用异步生成器函数
(async () => {
    const gen = asyncGenerator();
    while (true) {
        const result = await gen.next();
        if (result.done) break;
        console.log(result.value);
    }
})();

在这个例子中,我们使用了 gen.next() 来手动控制异步生成器的执行。每次调用 gen.next() 时,生成器函数会等待异步操作完成,并返回下一个 yield 的值。当生成器函数完成时,result.done 会变为 true,我们可以据此退出循环。

总结

生成器函数(包括同步和异步生成器函数)在 TypeScript 中是一种强大的工具,特别是在需要逐步处理数据流或异步操作时非常有用。通过 yield 关键字,生成器函数可以暂停执行并在需要时恢复执行,而 await 关键字则可以与异步操作结合使用,使得异步代码的处理更加简洁和直观。

;