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

TS面试真题详解与实战演练

标签:
面试
概述

掌握TypeScript(TS)对于提升前端开发能力和求职竞争力至关重要,TS面试真题涵盖了从基础类型与接口到高级特性的各个方面。通过这些真题,你可以深入理解TS的特性和应用,从而在实际工作中编写更安全、更高效的代码。熟练掌握TS面试真题不仅能帮助你通过面试,还能提升你的编程水平。

TS面试真题简介

TS面试真题的重要性

在现代软件开发中,TypeScript(简称TS)因其静态类型检查和面向对象编程特性,被广泛应用于前端开发。掌握TypeScript不仅可以帮助开发者编写更安全、更健壮的代码,还能在求职过程中提升竞争力。因此,TS面试成为许多前端面试中不可或缺的一部分。通过熟练掌握TS面试题,你不仅能够提升自己对TS的理解,还能在实际工作中更好地利用其特性。

面试官常问的TS问题类型

  1. 基础知识:如变量类型、基本数据类型、接口等。
  2. 高级特性:如泛型、装饰器等。
  3. 面向对象编程:如类、继承、抽象类等。
  4. 类型检查与推断:如联合类型、交叉类型、类型推断等。
  5. 代码示例与实践:如如何编写高性能的TS代码、如何调试TS代码等。

基础类型与接口

常见的数据类型

TypeScript 支持多种数据类型,包括基本数据类型、复合数据类型和特殊类型。这些类型在日常开发中经常使用,掌握这些类型有助于编写更准确的代码。

  1. 基本数据类型

    • number:用于表示数值,可以是整数或浮点数。
    • string:用于表示字符串。
    • boolean:用于表示布尔值,只有 truefalse
    • symbol:表示独一无二的值。
    • undefined:表示未赋值的值。
    • null:表示空值。
  2. 复合数据类型
    • array:用于表示数组。
    • tuple:用于表示固定长度的数组,每个元素都有特定的类型。
    • enum:用于表示枚举类型。
    • any:表示任意类型。
    • void:表示没有返回值的函数。
    • never:表示永远不会返回的函数或总是抛出异常的操作。
let num: number = 10;
let str: string = "Hello, TypeScript!";
let bool: boolean = true;
let sym: symbol = Symbol('unique value');
let undef: undefined = undefined;
let nullVal: null = null;
let arr: number[] = [1, 2, 3];
let tupleArr: [number, string, boolean] = [1, "two", false];
let enumType: MyEnum = MyEnum.One;
let anyVar: any = 10;
let voidFunc: () => void = () => {};
let neverFunc: () => never = () => { throw new Error("error") };
let obj: object = { key: "value" };

接口的概念与使用

在TypeScript中,接口用于定义对象的结构。接口定义了一组属性、方法和索引签名,但不实现它们。定义接口有助于确保对象符合预期的结构,从而提高代码的可读性和可维护性。

  1. 简单接口:定义对象的属性和方法。
interface SimpleInterface {
    name: string;
    age: number;
    greet(): void;
}

let person: SimpleInterface = {
    name: "Alice",
    age: 30,
    greet() {
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
    }
};
  1. 索引签名:定义对象的索引类型。
interface Indexable {
    [index: string]: string;
}

let myObj: Indexable = {
    a: "value1",
    b: "value2"
};
  1. 可选属性:允许某些属性可选。
interface OptionalInterface {
    name?: string;
    age?: number;
}

let personOptional: OptionalInterface = {};
  1. 只读属性:确保对象的属性在初始化后不能被修改。
interface ReadonlyInterface {
    readonly name: string;
}

let personReadonly: ReadonlyInterface = { name: "Alice" };
personReadonly.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.
  1. 方法重载:定义多个方法签名以实现方法重载。
interface MethodOverload {
    (name: string): number;
    (name: string, age: number): string;
}

let overloadFunc: MethodOverload = (name: string, age?: number): number | string => {
    if (age === undefined) {
        return name.length;
    } else {
        return `${name} is ${age} years old.`;
    }
};

console.log(overloadFunc("Alice")); // 输出 "5"
console.log(overloadFunc("Alice", 30)); // 输出 "Alice is 30 years old."

题目示例与解析

示例1:定义一个包含姓名、年龄和问候方法的简单接口,并实现一个符合该接口的对象。

interface PersonInterface {
    name: string;
    age: number;
    greet(): void;
}

let person: PersonInterface = {
    name: "Alice",
    age: 30,
    greet() {
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
    }
};

person.greet(); // 输出 "Hello, my name is Alice and I'm 30 years old."

示例2:定义一个包含索引签名的接口,并实现一个符合该接口的对象。

interface IndexInterface {
    [index: string]: string;
}

let myIndex: IndexInterface = {
    a: "value1",
    b: "value2"
};

console.log(myIndex["a"]); // 输出 "value1"

类与继承

类的定义与使用

在TypeScript中,类是面向对象编程的核心概念之一。类允许你定义对象的结构和行为。类可以分为实例方法、静态方法、实例属性和静态属性。

  1. 实例方法和属性
class Car {
    color: string;
    constructor(color: string) {
        this.color = color;
    }

    honk(): void {
        console.log("Honk!");
    }
}

let myCar: Car = new Car("red");
console.log(myCar.color); // 输出 "red"
myCar.honk(); // 输出 "Honk!"
  1. 静态方法和属性
class MathUtil {
    static add(a: number, b: number): number {
        return a + b;
    }

    static PI = 3.14;
}

console.log(MathUtil.add(2, 3)); // 输出 "5"
console.log(MathUtil.PI); // 输出 "3.14"

继承的概念与实现

继承允许你创建一个新的类,该类基于现有类(父类)并可以继承其属性和方法。这有助于代码重用和层次结构的创建。继承使用 extends 关键字来实现。

  1. 继承和重写方法
class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }

    makeSound(): void {
        console.log("Animal sound");
    }
}

class Dog extends Animal {
    makeSound(): void {
        console.log("Woof!");
    }
}

let myDog: Dog = new Dog("Buddy");
console.log(myDog.name); // 输出 "Buddy"
myDog.makeSound(); // 输出 "Woof!"
  1. 访问基类的方法和属性
class Base {
    protected name: string;
    constructor(name: string) {
        this.name = name;
    }

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

class Derived extends Base {
    getProtectedName(): string {
        return this.getName();
    }
}

let derivedObj: Derived = new Derived("Alice");
console.log(derivedObj.getProtectedName()); // 输出 "Alice"
  1. 抽象类:定义抽象类使用 abstract 关键字。
abstract class AbstractClass {
    abstract getName(): string;
}

class ConcreteClass extends AbstractClass {
    getName(): string {
        return "Alice";
    }
}

let concreteObj: ConcreteClass = new ConcreteClass();
console.log(concreteObj.getName()); // 输出 "Alice"

题目示例与解析

示例1:定义一个 Base 类,并定义一个 Derived 类继承 Base 类,并实现抽象方法。

abstract class Base {
    abstract getName(): string;
}

class Derived extends Base {
    name: string;
    constructor(name: string) {
        super();
        this.name = name;
    }

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

let derivedObj: Derived = new Derived("Alice");
console.log(derivedObj.getName()); // 输出 "Alice"

示例2:定义一个 BaseEmployee 类,并定义一个 Manager 类继承 BaseEmployee 类,并实现抽象方法。

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

    abstract calculateSalary(): number;
}

class Manager extends BaseEmployee {
    salary: number;
    constructor(name: string, salary: number) {
        super(name);
        this.salary = salary;
    }

    calculateSalary(): number {
        return this.salary * 1.5;
    }
}

let manager: Manager = new Manager("Alice", 5000);
console.log(manager.calculateSalary()); // 输出 "7500"

泛型与模板

泛型的基本概念

泛型是一种强大的编程技术,它允许你定义可复用的组件,这些组件可以处理多种类型的数据,而无需具体指定类型。泛型使用 <> 来定义类型参数。

  1. 简单泛型
function identity<T>(arg: T): T {
    return arg;
}

let numResult = identity<number>(10);
let strResult = identity<string>("Hello");

console.log(numResult); // 输出 "10"
console.log(strResult); // 输出 "Hello"
  1. 泛型接口
interface GenericIdentity<T> {
    (arg: T): T;
}

let identityFunc: GenericIdentity<number> = (num: number) => num;

console.log(identityFunc(10)); // 输出 "10"
  1. 泛型类
class GenericClass<T> {
    value: T;

    constructor(val: T) {
        this.value = val;
    }

    identity(): T {
        return this.value;
    }
}

let numClass = new GenericClass<number>(10);
console.log(numClass.identity()); // 输出 "10"

泛型的应用场景

泛型在开发中有很多应用场景,包括但不限于:

  1. 数组操作
    • 使用泛型定义数组,确保数组中的元素类型一致。
let numbers: Array<number> = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];
  1. 函数返回类型
    • 使用泛型定义函数返回类型,确保返回值类型与传入参数类型一致。
function identity<T>(arg: T): T {
    return arg;
}

let numResult = identity<number>(10);
let strResult = identity<string>("Hello");

console.log(numResult); // 输出 "10"
console.log(strResult); // 输出 "Hello"
  1. 自定义集合类
    • 使用泛型定义自定义集合类,使其能够存储多种类型的数据。
class MyCollection<T> {
    private items: T[] = [];

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

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

let intCollection = new MyCollection<number>();
intCollection.add(1);
intCollection.add(2);

let strCollection = new MyCollection<string>();
strCollection.add("a");
strCollection.add("b");

console.log(intCollection.getItems()); // 输出 "[1, 2]"
console.log(strCollection.getItems()); // 输出 '["a", "b"]'

题目示例与解析

示例1:定义一个泛型函数,该函数返回传入参数的值。

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

let numResult = identity<number>(10);
let strResult = identity<string>("Hello");

console.log(numResult); // 输出 "10"
console.log(strResult); // 输出 "Hello"

示例2:定义一个泛型接口,该接口包含一个返回类型为泛型参数的方法。

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

let identityFunc: GenericIdentity<number> = (num: number) => num;

console.log(identityFunc(10)); // 输出 "10"

示例3:定义一个泛型类,该类包含一个返回类型为泛型参数的方法。

class GenericClass<T> {
    value: T;

    constructor(val: T) {
        this.value = val;
    }

    identity(): T {
        return this.value;
    }
}

let numClass = new GenericClass<number>(10);
console.log(numClass.identity()); // 输出 "10"

高级特性

TypeScript的高级特性介绍

TypeScript 提供了一些高级特性,可以进一步提高代码的灵活性和可维护性。这些特性包括:

  1. 装饰器:装饰器允许你为类或其成员添加额外的功能。
  2. 模板字符串:可以嵌入表达式和变量。
  3. 映射类型:可以对现有类型进行转换或修改。
  4. 类型保护:确保对类型进行更严格的检查。
  5. 重写继承的属性:可以覆盖基类中的属性。

如何在实践中应用这些特性

为了更好地应用这些特性,开发者需要理解其工作原理以及如何在实际项目中使用它们。以下是一些示例:

  1. 装饰器
function readonly<T extends object, P extends keyof T>(target: T, propName: P): void {
    Object.defineProperty(target, propName, {
        writable: false,
        configurable: false,
    });
}

class Test {
    @readonly
    public property: string = "test";

    setProperty(value: string) {
        this.property = value; // Error: Cannot assign to 'property' because it is a read-only property.
    }
}

let test = new Test();
  1. 模板字符串
let name: string = "Alice";
let greeting: string = `Hello, ${name}!`;

console.log(greeting); // 输出 "Hello, Alice!"
  1. 映射类型
interface OriginalType {
    a: number;
    b: string;
}

type MappedType = {
    [P in keyof OriginalType]: OriginalType[P];
};

let mappedObj: MappedType = { a: 1, b: "value" };
  1. 类型保护
function isString(arg: any): arg is string {
    return typeof arg === "string";
}

function process(arg: any) {
    if (isString(arg)) {
        console.log(arg.toUpperCase());
    } else {
        console.log(arg);
    }
}

process("hello"); // 输出 "HELLO"
process(123);    // 输出 "123"
  1. 重写继承的属性
class Base {
    protected name: string = "Base";
}

class Derived extends Base {
    protected name: string = "Derived";
}

let derived = new Derived();
console.log(derived.name); // 输出 "Derived"

题目示例与解析

示例1:定义一个装饰器,该装饰器将属性标记为只读。

function readonly<T extends object, P extends keyof T>(target: T, propName: P): void {
    Object.defineProperty(target, propName, {
        writable: false,
        configurable: false,
    });
}

class Test {
    @readonly
    public property: string = "test";

    setProperty(value: string) {
        this.property = value; // Error: Cannot assign to 'property' because it is a read-only property.
    }
}

let test = new Test();

示例2:定义一个类型保护函数,该函数检查给定参数是否为字符串。

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

function process(arg: any) {
    if (isString(arg)) {
        console.log(arg.toUpperCase());
    } else {
        console.log(arg);
    }
}

process("hello"); // 输出 "HELLO"
process(123);    // 输出 "123"

示例3:定义一个映射类型,该类型将原始类型中的每个属性类型保持不变。

interface OriginalType {
    a: number;
    b: string;
}

type MappedType = {
    [P in keyof OriginalType]: OriginalType[P];
};

let mappedObj: MappedType = { a: 1, b: "value" };

示例4:定义一个类,并覆盖继承的属性。

class Base {
    protected name: string = "Base";
}

class Derived extends Base {
    protected name: string = "Derived";
}

let derived = new Derived();
console.log(derived.name); // 输出 "Derived"

实战演练与面试技巧

如何准备TS面试

准备TS面试需要从多个方面入手,包括理论知识的掌握、实践经验的积累和面试技巧的培养。

  1. 理论知识

    • 掌握基础知识:了解TS的基本概念,如变量类型、函数、类、接口等。
    • 深入学习高级特性:掌握泛型、装饰器、类型保护等高级特性。
    • 阅读官方文档:熟悉TS的官方文档,了解最新的特性和最佳实践。
  2. 实践经验

    • 编写代码:通过编写实际代码来加深对TS的理解。
    • 参与开源项目:参与开源项目可以提升你的实战能力。
    • 练习面试题:多做面试题,熟悉面试中常见的问题和解决方法。
  3. 面试技巧
    • 准备常见问题:熟悉面试中常见问题的解答。
    • 模拟面试:通过模拟面试来提升自己的表现。
    • 保持自信:保持自信,从容应对面试中的各种问题。

面试中的常见技巧与注意事项

  1. 清晰表达

    • 在回答问题时,尽量使用清晰、简洁的语言,避免使用过于复杂的术语。
    • 对于复杂的问题,可以分步骤来解释你的思路和答案。
  2. 逻辑严谨

    • 在回答问题时,保持逻辑严谨,确保回答的准确性。
    • 避免在回答问题时出现逻辑上的漏洞。
  3. 积极参与

    • 在面试过程中,积极参与讨论,展示你的思考过程。
    • 对于你不熟悉的问题,可以提出自己的理解和看法。
  4. 时间管理
    • 在回答问题时,注意时间管理,确保每个问题的回答时间适当。
    • 避免在某个问题上花费过多时间,导致其他问题的回答时间不足。

面试真题实战演练

为了更好地准备TS面试,以下是一些面试真题的实战演练,这些题目涵盖了TS的基础和高级特性。

示例1:定义一个函数,该函数返回传入参数的类型。

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

let num: number = identity<number>(10);
let str: string = identity<string>("Hello");

console.log(num); // 输出 "10"
console.log(str); // 输出 "Hello"

示例2:定义一个泛型接口,该接口包含一个返回类型为泛型参数的方法。

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

let identityFunc: GenericIdentity<number> = (num: number) => num;

console.log(identityFunc(10)); // 输出 "10"

示例3:定义一个类,该类包含一些属性和方法,并实现继承。

class Base {
    protected name: string = "Base";
}

class Derived extends Base {
    protected name: string = "Derived";
}

let derived = new Derived();
console.log(derived.name); // 输出 "Derived"

示例4:定义一个装饰器,该装饰器将属性标记为只读。

function readonly<T extends object, P extends keyof T>(target: T, propName: P): void {
    Object.defineProperty(target, propName, {
        writable: false,
        configurable: false,
    });
}

class Test {
    @readonly
    public property: string = "test";

    setProperty(value: string) {
        this.property = value; // Error: Cannot assign to 'property' because it is a read-only property.
    }
}

let test = new Test();
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
205
获赞与收藏
1008

关注作者,订阅最新文章

阅读免费教程

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消