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

TS面试题详解与解答

概述

本文全面介绍了TypeScript的基础概念和高级特性,包括类型系统、函数和对象类型,以及常见的TS面试题解析。文章还深入探讨了装饰器、模块和异步编程等高级特性,并通过实战案例分享和面试技巧建议,帮助读者更好地理解和掌握TypeScript。文中详细解答了TS面试题,提供了丰富的示例代码和实践建议。

TS基础概念理解

TypeScript(简称TS)是由微软开发并维护的一种静态类型语言,其语法与JavaScript非常相似,但加入了类型检查机制,以增强代码的可读性、可维护性和可预测性。TypeScript编译器会将代码转换为JavaScript代码,因此任何现有的JavaScript代码都可以作为TypeScript代码使用。TypeScript的目标是提高JavaScript的开发效率,同时保持JavaScript的灵活性和强大功能。

1. 变量与类型

在TypeScript中,可以在声明变量时指定类型,以确保编译时进行类型检查。TypeScript支持多种基本类型,包括但不限于:numberstringbooleanundefinednullvoid等。此外,还有更复杂的类型如数组、元组、枚举等。

基本类型示例:

let num: number = 10;
let str: string = "Hello";
let bool: boolean = true;
let und: undefined = undefined;
let nul: null = null;
let voidType: void = undefined; // void 类型只能赋值为 undefined 或 null

数组类型示例:

let numArray: number[] = [1, 2, 3];
let arr: Array<number> = [1, 2, 3]; // 使用 Array 类型

元组类型示例:

let tuple: [number, string] = [1, "hello"];

枚举类型示例:

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

2. 函数类型

在TypeScript中,函数也是一种类型。函数类型可以指定参数类型和返回类型。以下是函数类型的定义示例:

// 定义一个函数类型,接受一个 number 类型的参数,返回一个 string
type FuncType = (num: number) => string;

let func: FuncType = (num: number): string => {
  return num.toString();
};

// 使用函数声明
function add(a: number, b: number): number {
  return a + b;
}

3. 对象类型

对象类型用于描述对象的结构,即对象中的属性和方法。可以使用接口(interface)或类型(type)来定义对象类型。

接口示例:

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

let person: Person = {
  name: "John",
  age: 30
};

类型(type)示例:

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

let point: Coordinate = {
  x: 10,
  y: 20
};

TS类型系统入门

TypeScript的类型系统是其核心部分,提供了丰富的类型定义来确保代码的类型正确性。主要类型系统包括联合类型、类型保护、类型推断和类型兼容性。

1. 联合类型

联合类型是指一个变量可以同时拥有多种类型的值。例如,一个变量可以是字符串或数字。

let value: string | number = "hello";
value = 10; // 合法

2. 类型保护

类型保护是一种在运行时确保变量具有特定类型的机制。常见的类型保护方法包括 typeofinstanceofin 操作符等。

typeof 示例:

function isString(value: any): value is string {
  return typeof value === "string";
}

let x: string | number = "hello";
if (isString(x)) {
  console.log(x.toUpperCase());
} else {
  console.log(x.toFixed());
}

instanceof 示例:

class Animal {
  name: string;
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name} says woof!`);
  }
}

function isDog(animal: Animal): animal is Dog {
  return animal instanceof Dog;
}

let animal: Animal = new Dog();
if (isDog(animal)) {
  animal.bark();
}

3. 类型推断

TypeScript能够根据赋值自动推断出变量的类型。

let num = 10; // 类型推断为 number
let str = "hello"; // 类型推断为 string

4. 类型兼容性

TypeScript的类型兼容性规则定义了两个类型是否可以互相赋值。类型兼容性包括结构兼容性、赋值兼容性和函数兼容性等。

结构兼容性示例:

interface User {
  name: string;
  age?: number;
}

let user: User = {
  name: "John"
};

user = {
  name: "Jane",
  age: 25
}; // 合法,因为第二个对象包含第一个对象的结构

常见TS面试问题解析

  1. 什么是类型推断?
    类型推断是指编译器根据变量的赋值自动推断其类型的过程。例如,let num = 10;,编译器会推断此变量类型为number

  2. TypeScript中如何处理继承?
    TypeScript支持类的继承。通过extends关键字可以实现类的继承。

    class Animal {
     protected name: string;
     constructor(name: string) {
       this.name = name;
     }
     greet() {
       console.log("Hello, I'm " + this.name);
     }
    }
    
    class Dog extends Animal {
     bark() {
       console.log(this.name + " says woof");
     }
    }
    
    let dog = new Dog("Rex");
    dog.greet(); // 输出 "Hello, I'm Rex"
    dog.bark();  // 输出 "Rex says woof"
  3. 实现接口与类的区别?
    接口定义了类必须实现的成员,而类可以实现多个接口。接口主要用于描述对象的结构,而类可以包含实现和状态。

    interface Loggable {
     log(): void;
    }
    
    class ConsoleLogger implements Loggable {
     log() {
       console.log("logging");
     }
    }
    
    let logger: Loggable = new ConsoleLogger();
    logger.log(); // 输出 "logging"
  4. 泛型的作用和使用?
    泛型允许编写可以应用于多种类型的代码,提高复用性。

    function identity<T>(arg: T): T {
     return arg;
    }
    
    let outputIdentity = identity<string>("Hello"); // 输出 "Hello"
  5. 类型断言是什么?
    类型断言是明确告诉编译器如何将一个类型转换为另一个类型的语法。分为两种形式,一种是<Type>,另一种是as Type

    let someValue: unknown = "hello";
    let safeValue: string = someValue as string;

TS高级特性介绍

  1. 装饰器 (Decorators)

    装饰器是用于装饰类、方法、访问符或属性的函数。装饰器是一种特殊类型的声明,可以被附加到类声明、方法、访问符、属性或参数上。它们可以用来定义元数据或修改类的行为。

    function readonly(target: any, key: string) {
     let value = target[key];
     let writableDescriptor: PropertyDescriptor = {
       get() {
         return value;
       },
       set() {
         throw new Error("Can't modify a readonly property");
       }
     };
     Object.defineProperty(target, key, writableDescriptor);
    }
    
    class Greeter {
     @readonly
     greeting = "Hello";
    
     greet() {
       return this.greeting;
     }
    }
    
    let greeter = new Greeter();
    console.log(greeter.greet()); // 输出 "Hello"
    greeter.greeting = "Goodbye"; // 抛出错误 "Can't modify a readonly property"
  2. 模块 (Modules)

    TypeScript支持ES6模块系统,允许通过importexport关键字进行模块化开发。这种方式提高了代码的复用性和可维护性。以下是一个更复杂的模块示例,展示了如何在实际项目中使用模块:

    // greeter.ts
    export class Greeter {
     greeting = "Hello";
     greet() {
       return this.greeting;
     }
    }
    
    // main.ts
    import { Greeter } from "./greeter";
    
    let greeter = new Greeter();
    console.log(greeter.greet()); // 输出 "Hello"
    
    // 处理错误示例
    try {
     console.log(greeter.greet());
    } catch (error) {
     console.error("Error:", error);
    }
  3. 异步编程(Async / Await)

    TypeScript支持最新的ES2017异步编程语法,如async/await。这使异步代码的编写更加简洁和易读。以下是一个更复杂的异步编程示例,展示了如何在实际项目中使用async/await

    async function fetchUser(id: number): Promise<{ id: number; name: string }> {
     return new Promise((resolve) => {
       setTimeout(() => {
         resolve({ id, name: "John" });
       }, 100);
     });
    }
    
    async function printUser() {
     try {
       const user = await fetchUser(1);
       console.log(user.name);
     } catch (error) {
       console.error("Error fetching user:", error);
     }
    }
    
    printUser(); // 输出 "John"

实战案例分享

案例1:使用TypeScript实现一个简单的Todo List应用

需求描述:

  1. 用户能够添加新的待办事项。
  2. 用户能够删除已有的待办事项。
  3. 应用应该能够持久化存储待办事项。
  4. 应用还应支持更新待办事项的状态。

实现步骤:

  1. 定义一个待办事项的类型。
  2. 使用类和接口实现待办事项的管理功能。
  3. 使用LocalStorage存储待办事项。
  4. 添加更新待办事项状态的功能。

代码实现:

interface TodoItem {
  id: number;
  text: string;
  completed: boolean;
}

class TodoManager {
  private todos: TodoItem[] = [];

  addTodo(text: string): TodoItem {
    const id = this.todos.length + 1;
    const todo = { id, text, completed: false };
    this.todos.push(todo);
    this.saveTodos();
    return todo;
  }

  deleteTodo(id: number): void {
    this.todos = this.todos.filter(todo => todo.id !== id);
    this.saveTodos();
  }

  updateTodo(id: number, text: string, completed: boolean): void {
    const todo = this.todos.find(todo => todo.id === id);
    if (todo) {
      todo.text = text;
      todo.completed = completed;
      this.saveTodos();
    }
  }

  getTodos(): TodoItem[] {
    return this.todos;
  }

  private saveTodos(): void {
    localStorage.setItem("todos", JSON.stringify(this.todos));
  }

  private loadTodos(): void {
    const todos = localStorage.getItem("todos");
    if (todos) {
      this.todos = JSON.parse(todos);
    }
  }

  constructor() {
    this.loadTodos();
  }
}

const manager = new TodoManager();

manager.addTodo("Learn TypeScript");
manager.addTodo("Write a TypeScript application");

console.log(manager.getTodos());

manager.deleteTodo(1);

console.log(manager.getTodos());

manager.updateTodo(2, "Build a TypeScript-based web app", true);

console.log(manager.getTodos());

案例2:实现一个简单的REST API Server

需求描述:

  1. 使用Express框架搭建一个简单的REST API服务器。
  2. API应支持增删改查操作。
  3. 使用TypeScript定义接口和响应体。
  4. 添加错误处理逻辑。

实现步骤:

  1. 设置项目环境,安装相关依赖。
  2. 定义API接口。
  3. 实现增删改查逻辑。
  4. 添加错误处理逻辑。

代码实现:

import express, { Request, Response } from "express";
import bodyParser from "body-parser";

interface TodoItem {
  id: number;
  text: string;
  completed: boolean;
}

const app = express();
app.use(bodyParser.json());

const todos: TodoItem[] = [];

// GET /todos 获取所有待办事项
app.get("/todos", (req: Request, res: Response) => {
  res.json(todos);
});

// POST /todos 添加新的待办事项
app.post("/todos", (req: Request, res: Response) => {
  const todo: TodoItem = {
    id: todos.length + 1,
    text: req.body.text,
    completed: false
  };
  todos.push(todo);
  res.status(201).json(todo);
});

// PUT /todos/:id 更新待办事项
app.put("/todos/:id", (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const todo = todos.find(t => t.id === id);
  if (todo) {
    todo.text = req.body.text;
    todo.completed = req.body.completed;
    res.json(todo);
  } else {
    res.status(404).json({ error: "Not found" });
  }
});

// DELETE /todos/:id 删除待办事项
app.delete("/todos/:id", (req: Request, res: Response) => {
  const id = Number(req.params.id);
  todos = todos.filter(t => t.id !== id);
  res.status(204).send();
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

// 错误处理示例
app.use((err: any, req: Request, res: Response, next: any) => {
  console.error("Error:", err.stack);
  res.status(500).json({ error: "Internal server error" });
});

面试技巧与准备建议

  1. 深入理解基础概念:确保对变量、类型、函数、类等基础概念有深入的理解。理解类型系统中的联合类型、类型保护和类型兼容性等高级特性。
  2. 动手实践:通过实际项目或练习来巩固所学知识。TypeScript提供了丰富的类型定义和高级特性,动手实践可以帮助你更好地理解这些特性。
  3. 熟悉框架和库的类型定义:了解如何使用TypeScript的类型定义来使用流行的JavaScript库,如React、Vue等。
  4. 了解TypeScript的编译器配置:掌握如何配置tsconfig.json,以便更好地控制编译选项和输出。
  5. 掌握常用的设计模式:了解一些常见的设计模式在TypeScript中的实现,例如工厂模式、装饰器模式等。
  6. 学习异步编程:掌握TypeScript的Promise、async/await等异步编程特性。这在实际开发中非常重要。
  7. 持续跟进最新技术:TypeScript是一个快速发展的领域,时常查阅官方文档和社区资源,跟进最新特性和技术趋势。
  8. 编写高质量代码:注重代码的可读性,利用类型系统来提前发现潜在的错误。编写简洁明了的代码,提高团队协作效率。
  9. 准备面试题:提前准备一些常见面试题,包括基础概念、类型系统、高级特性等。练习解释这些概念,确保在面试时能够清晰表达。
  10. 理论与实践结合:理论知识固然重要,但理论与实践相结合才能更好地掌握TypeScript。在项目实践中不断积累经验,提高代码质量和解决问题的能力。
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消