为了账号安全,请及时绑定邮箱和手机立即绑定

TypeScript高级知识入门教程

概述

本文深入探讨了TypeScript中的高级知识,包括接口与类型别名的区别、泛型的概念与应用实例、装饰器的入门介绍与使用,以及高级类型详解等,旨在帮助开发者更好地理解和运用这些高级特性。通过详细示例代码,文章进一步说明了如何在实际项目中应用这些知识。此外,文章还介绍了TypeScript的模块化管理和配置文件的使用方法,以提升代码的组织和管理能力。文中涵盖了丰富的类型定义和模块系统配置,使读者能够全面掌握TypeScript高级知识。

TypeScript高级知识入门教程
TypeScript接口与类型区别解析

接口

在TypeScript中,接口(Interface)是一种定义对象的契约,用来描述对象的结构和行为。接口主要用来定义对象的形状,但不定义具体的方法实现。以下是接口的定义和使用示例:

interface Person {
    name: string;
    age: number;
    sayHello(): void;
}

let user: Person = {
    name: "张三",
    age: 25,
    sayHello: function() {
        console.log(`Hello, I'm ${this.name}`);
    }
};

user.sayHello();  // 输出:Hello, I'm 张三

类型别名

类型别名(Type Alias)则提供了一种为现有类型分配新名称的方式。类型别名可以是任何类型,包括对象类型、联合类型和元组类型等。以下是类型别名的定义和使用示例:

type PersonType = {
    name: string;
    age: number;
};

let user: PersonType = {
    name: "李四",
    age: 30
};

console.log(user.name);  // 输出:李四

接口与类型别名的区别

  1. 可扩展性:接口可以被继承,而类型别名不能。
  2. 方法实现:接口可以定义方法的签名,但不能定义方法的实现。类型别名可以包含函数的完整实现。
  3. 读取性:接口通常用于定义对象结构,而类型别名更适用于定义简单类型或复杂类型的别名。

示例代码

// 定义接口
interface Vehicle {
    drive(): void;
}

// 定义类型别名
type VehicleType = {
    drive: () => void;
};

// 使用接口
class Car implements Vehicle {
    drive() {
        console.log("Car is driving.");
    }
}

let car: Vehicle = new Car();
car.drive();  // 输出:Car is driving.

// 使用类型别名
let carType: VehicleType = {
    drive: function() {
        console.log("Car is driving with type alias.");
    }
};

carType.drive();  // 输出:Car is driving with type alias.
泛型的概念与应用实例

泛型定义

泛型(Generics)允许开发者编写可重用的代码块,这些代码块可以处理多种类型的数据。通过使用泛型,可以增强代码的灵活性和重用性。

泛型函数

示例代码

function identity<T>(arg: T): T {
    return arg;
}

let output = identity<string>("Hello, world!");
console.log(output);  // 输出:Hello, world!

泛型接口

泛型接口允许定义一组数据类型,而不必指定具体的类型。这提高了代码的灵活性和可重用性。

示例代码

interface GenericIdentity<T> {
    (arg: T): T;
}

let identity: GenericIdentity<string> = (arg: string) => arg;
console.log(identity("Hello"));  // 输出:Hello

泛型类

泛型类允许在类中使用泛型,这样可以创建更通用的类,用于处理多种类型的对象。

示例代码

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;

    constructor(zeroValue: T, add: (x: T, y: T) => T) {
        this.zeroValue = zeroValue;
        this.add = add;
    }
}

let stringNumeric = new GenericNumber<string>("", (x, y) => x + y);
console.log(stringNumeric.add("Hello, ", "world!"));  // 输出:Hello, world!

泛型约束

通过泛型约束,可以在泛型中添加约束条件,使泛型更加灵活。约束条件可以是接口或其他类型。

示例代码

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // 仅约束为具有 length 属性的对象
    return arg;
}

let arr = loggingIdentity([1, 2, 3]);
let str = loggingIdentity("Hello");
装饰器的入门介绍与使用

装饰器定义

装饰器是一种特殊类型的声明,可以在运行时修改类的行为。装饰器可以被应用于类、方法、访问器、属性或参数。

装饰器类型

类装饰器

类装饰器用于装饰类声明。

方法装饰器

方法装饰器用于装饰声明或方法重写。

属性装饰器

属性装饰器用于装饰属性或索引签名。

参数装饰器

参数装饰器用于装饰类的构造函数参数。

示例代码

function readonly(target: any, propertyName: string) {
    let value: any;
    Object.defineProperty(target, propertyName, {
        get: () => value,
        set: (newValue) => {
            if (value === undefined) {
                value = newValue;
            } else {
                throw new Error("Cannot set property, it is readonly");
            }
        }
    });
}

class Example {
    @readonly
    public readonlyProperty: string = "Hello";

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

const example = new Example("World");
console.log(example.readonlyProperty);  // 输出:World
高级类型详解:映射类型与条件类型

映射类型

映射类型用于基于现有类型生成新类型。映射类型可以应用于现有类型的所有属性,例如添加修饰符、更改类型或添加新属性。

映射类型的一个重要应用场景是将现有对象的所有属性转换为只读属性。例如:

interface Person {
    name: string;
    age: number;
}

type ReadonlyPerson = {
    readonly [P in keyof Person]: Person[P];
};

let person: ReadonlyPerson = {
    name: "Alice",
    age: 30
};

// person.name = "Bob";  // 错误:无法重新赋值只读属性

条件类型

条件类型用于基于类型之间的关系生成新类型。条件类型可以基于类型是否为特定类型来选择不同的类型。

条件类型的一个典型应用场景是根据类型是否为字符串来决定是否为真:

type IsString<T> = T extends string ? true : false;

type test1 = IsString<"Hello">;  // 输出:true
type test2 = IsString<123>;      // 输出:false

条件类型与泛型结合的示例

条件类型可以与泛型结合使用,以创建更复杂的类型判断。例如,以下示例展示了如何根据类型是否为字符串来决定返回类型:

type ToLowerCase<T extends string> = T extends `${infer First}${infer Rest}`
    ? First extends Uppercase<First>
        ? `${Lowercase<First>}${ToLowerCase<Rest>}`
        : `${First}${ToLowerCase<Rest>}`
    : T;

type result = ToLowerCase<"HelloWorld">;  // 输出:helloworld
代码组织与模块化管理

模块系统

TypeScript支持多种模块系统,如CommonJS、AMD、ES6和UMD。模块系统允许通过importexport来组织和管理代码。

示例代码

// 文件:math.ts
export function add(a: number, b: number): number {
    return a + b;
}

// 文件:main.ts
import { add } from "./math";

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

模块别名

模块别名允许使用别名来引用模块,使得代码更具可读性。

示例代码

// 文件:main.ts
import * as math from "./math";

console.log(math.add(2, 3));  // 输出:5

配置tsconfig.json

通过配置tsconfig.json文件,可以定义项目的编译选项和模块系统。

示例代码

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "outDir": "./dist",
        "strict": true
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules"]
}

模块化结构

通过合理的模块化结构,可以有效组织代码,提高代码的可维护性和可测试性。

示例代码结构

src/
├── math/
│   ├── math.ts
│   └── index.ts
└── main.ts

示例代码

// 文件:src/math/math.ts
export function add(a: number, b: number): number {
    return a + b;
}

// 文件:src/math/index.ts
export * from "./math";

// 文件:src/main.ts
import { add } from "./math";

console.log(add(2, 3));  // 输出:5
TypeScript配置文件与编译选项详解

tsconfig.json

tsconfig.json是TypeScript项目的配置文件,定义了项目的编译选项、模块系统和其他设置。

示例代码

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "outDir": "./dist",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "allowJs": true,
        "noEmit": false
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "dist"]
}

编译选项

module

定义模块系统,支持commonjsamdes2020esnext等。

target

定义编译目标,支持es3es5es2015es2016等。

outDir

输出目录,指定编译后的文件保存的位置。

strict

启用严格类型检查,包含严格全局声明、严格函数类型等。

esModuleInterop

如果启用,则在ES模块系统中提供require函数。

skipLibCheck

跳过库文件的类型检查,提高编译速度。

forceConsistentCasingInFileNames

强制文件名一致的大小写,避免跨平台问题。

allowJs

允许编译JavaScript文件。

noEmit

不生成输出文件,仅用于类型检查。

示例代码

// 文件:src/main.ts
import { add } from "./math";

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

编译命令

使用tsc命令编译TypeScript文件。

tsc

示例代码输出

// 文件:dist/main.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.add = void 0;
function add(a, b) {
    return a + b;
}
console.log(add(2, 3));

配置环境变量

通过设置环境变量,可以控制TypeScript的编译行为。

示例代码

export TS_NODE_PROJECT=./tsconfig.json

代码分割

通过配置tsconfig.json中的module选项为esnext,可以支持代码分割功能。

示例代码

{
    "compilerOptions": {
        "module": "esnext",
        "target": "es6",
        "outDir": "./dist",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "allowJs": true,
        "noEmit": false
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "dist"]
}

示例代码输出

// 文件:dist/main.js
import { add } from "./math";
console.log(add(2, 3));
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消