继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

TypeScript高级知识入门详解

呼唤远方
关注TA
已关注
手记 356
粉丝 82
获赞 367
概述

本文详细介绍了TypeScript高级知识,包括泛型、高级类型、装饰器、Type Alias与Interface的区别,以及tsconfig.json的配置技巧。通过丰富的示例代码,帮助开发者深入理解并应用这些高级特性。文章还探讨了常见的编译问题及解决方法,旨在提高TypeScript项目的编译效率和代码质量。

TypeScript高级知识入门详解
TypeScript基础回顾

变量声明与类型注解

在TypeScript中,变量声明时可以使用letconstvar,而类型注解则用于给变量指定具体的类型。例如:

let age: number = 25;
const name: string = "Alice";
var isStudent: boolean = true;

// 动态类型推断
let someValue = "TypeScript";
someValue = 123; // 错误,不允许从字符串转换为数字

函数定义及参数类型

函数定义时需要明确返回类型和参数类型。例如:

function addNumbers(a: number, b: number): number {
    return a + b;
}

let result: number = addNumbers(4, 5);
console.log(result);

类与接口的基本使用

类和接口是TypeScript中封装数据和行为的重要方式。例如:

interface Person {
    firstName: string;
    lastName: string;
}

class Student implements Person {
    firstName: string;
    lastName: string;

    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName(): string {
        return `${this.firstName} ${this.lastName}`;
    }
}

let student = new Student("Alice", "Smith");
console.log(student.getFullName());
泛型的深入理解

泛型的定义与使用场景

泛型允许在定义函数、接口或类时使用类型参数。例如:

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

let output = identity<string>("TypeScript");
console.log(output);

泛型约束的实践

通过使用extends关键字可以为泛型添加类型约束。例如:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

let person = { name: "Alice", age: 25 };
let name = getProperty(person, "name");
console.log(name);

泛型在实际项目中的应用示例

泛型在实际项目中常用于创建通用的数据结构,如集合类。例如:

class Collection<T> {
    private items: T[] = [];

    add(item: T): void {
        this.items.push(item);
    }

    getItems(): T[] {
        return this.items;
    }
}

let stringCollection = new Collection<string>();
stringCollection.add("TypeScript");
console.log(stringCollection.getItems());
高级类型详解

联合类型与交叉类型的应用

联合类型和交叉类型是处理复杂类型组合的工具。例如:

let someValue: string | number;
someValue = "hello";
someValue = 123;

// 交叉类型
type A = { a: number };
type B = { b: string };
type AB = A & B;
let ab: AB = { a: 1, b: "hello" };

映射类型与条件类型

映射类型和条件类型用于创建更复杂的类型定义。例如:

type KeysToCamelCase<T> = {
    [P in keyof T as P extends string ? `${string & P}` : never]: T[P];
};

interface User {
    first_name: string;
    last_name: string;
}

type CamelCaseUser = KeysToCamelCase<User>;
let user: CamelCaseUser = { firstName: "Alice", lastName: "Smith" };

联合类型和交叉类型的实际问题解决

联合类型和交叉类型在处理复杂对象和数组时非常有用。例如:

type NullableString = string | null;
type NullableNumber = number | null;

type NullableStringOrNumber = NullableString | NullableNumber;
let value: NullableStringOrNumber = null;

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

type Intersect<A, B> = (A & B) extends infer I ? I : never;

type A = { a: string };
type B = { b: number };
type Intersected = Intersect<A, B>;

let intersected: Intersected = { a: "Alice", b: 123 };
TypeScript装饰器介绍

装饰器的工作原理

装饰器是函数,会接收一个或多个参数并在运行时应用到类、方法、属性或参数上。例如:

function log(target: any, key: string) {
    console.log(`Accessing ${key} property`);
}

class User {
    @log
    name: string = "Alice";
}

类装饰器、属性装饰器、方法装饰器的使用

装饰器可以应用于类、属性和方法。例如:

function readonly(target: any, key: string) {
    let value = target[key];
    let writable = true;

    const getter = () => value;
    const setter = (newvalue: any) => {
        if (writable) {
            value = newvalue;
        }
    };

    Object.defineProperty(target, key, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
    });
}

class User {
    @readonly
    name: string = "Alice";

    @log
    getName(): string {
        return this.name;
    }
}

const user = new User();
console.log(user.name); // 输出 "Alice"
user.name = "Bob"; // 不允许修改
console.log(user.getName()); // 输出 "Alice"

实际项目中的装饰器应用案例

装饰器在实际项目中常用于日志记录、权限验证等场景。例如:

function logMethod(target: any, name: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
        console.log(`Calling ${name} with`, args);
        return originalMethod.apply(this, args);
    };

    return descriptor;
}

class UserService {
    @logMethod
    getName(id: number): string {
        return `User ${id} Name`;
    }
}

let userService = new UserService();
console.log(userService.getName(123)); // 输出 "Calling getName with" 和 "User 123 Name"
Type Alias与Interface的区别与联系

Type Alias与Interface的定义

Type Alias(类型别名)和Interface(接口)都可以用来定义类型,但它们有一些细微的区别。例如:

type Point = {
    x: number;
    y: number;
};

interface PointInterface {
    x: number;
    y: number;
}

let point: Point = { x: 1, y: 2 };
let pointInterface: PointInterface = { x: 1, y: 2 };

区别与选择使用场景

Type Alias和Interface的主要区别在于Type Alias只能定义单一类型,而Interface可以逐步扩展。例如:

type Point1 = {
    x: number;
    y: number;
};

type Point2 = Point1 & {
    z: number;
};

interface Point3 {
    x: number;
    y: number;
}

interface Point3 {
    z: number;
}

let point1: Point1 = { x: 1, y: 2 };
let point2: Point2 = { x: 1, y: 2, z: 3 };
let point3: Point3 = { x: 1, y: 2, z: 3 };

实际编码中的应用比较

在实际编码中,如果需要定义一个单一类型,可以使用Type Alias,如果需要逐步扩展类型,建议使用Interface。例如:

type User = {
    id: number;
    name: string;
};

interface UserWithAge extends User {
    age: number;
}

let user: User = { id: 1, name: "Alice" };
let userWithAge: UserWithAge = { id: 1, name: "Alice", age: 25 };
TypeScript编译配置与高级设置

tsconfig.json文件详解

tsconfig.json文件用于配置TypeScript编译器的行为。例如:

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "strict": true,
        "esModuleInterop": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "baseUrl": "./",
        "paths": {
            "@utils/*": ["src/utils/*"]
        },
        "skipLibCheck": true,
        "allowJs": true,
        "noEmit": false,
        "sourceMap": true
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "**/*.spec.ts"]
}

编译选项的配置技巧

配置选项如targetmodulestrict等可以控制编译输出和类型检查的行为。例如:

{
    "compilerOptions": {
        "target": "ES2015",
        "module": "esnext",
        "strict": true,
        "esModuleInterop": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "baseUrl": "./",
        "paths": {
            "@utils/*": ["src/utils/*"]
        },
        "skipLibCheck": true,
        "allowJs": true,
        "noEmit": false,
        "sourceMap": true
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "**/*.spec.ts"]
}

常见问题及解决方法

一些常见的编译问题及其解决方法:

  • 问题1:无法解析模块
    • 解决方法:检查tsconfig.json中的paths配置是否正确。
  • 问题2:类型检查失败
    • 解决方法:确保所有必需的库和依赖已正确引入。
  • 问题3:编译错误
    • 解决方法:检查tsconfig.json配置是否正确,确保所有文件路径和模块路径正确。
  • 问题4:无法找到类型定义
    • 解决方法:安装对应的类型定义文件(如@types/<library-name>)。

例如,安装@types/node

npm install @types/node --save-dev

在实际项目中,合理配置tsconfig.json可以提高编译效率和代码质量。例如:

{
    "compilerOptions": {
        "target": "ES2015",
        "module": "esnext",
        "strict": true,
        "esModuleInterop": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "baseUrl": "./",
        "paths": {
            "@utils/*": ["src/utils/*"]
        },
        "skipLibCheck": true,
        "allowJs": true,
        "noEmit": false,
        "sourceMap": true
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "**/*.spec.ts"]
}
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP