【原生JS】 前端Typescript最常用 20 道面试题总结(含详细代码解析)

以下是老曹关于TypeScript 最常用的 20 道面试题总结,TS是原生Js的超集,现如今已经是前端面试特别是中高级面试必问的问题,也是大工程项目必须掌握的技术了,以下涵盖类型系统、接口、泛型、类、装饰器、模块等核心知识点。每道题都包含详细解释和代码示例,适合大家对前端开发岗位的 TypeScript 技术面试准备,大家可以随时翻出来查阅背诵和练习!


1. TypeScript 和 JavaScript 的区别是什么?

问题: 解释 TypeScript 相比 JavaScript 增加了哪些特性?

答案:

  • 类型系统(静态类型检查)。
  • 接口(Interface)与类型别名(Type)。
  • 泛型支持。
  • 装饰器(Decorators)。
  • 对象结构约束与可选属性。
  • 更好的 IDE 支持(自动补全、错误提示)。
// 示例:类型检查
function greet(name: string): void {
    console.log(`Hello, ${name}`);
}

greet("Tom"); // 正确
greet(123);   // 编译报错:参数类型不匹配

2. 如何定义变量并指定其类型?

问题: 定义一个 age 变量,并限制为数字类型。

答案:
使用类型注解语法。

let age: number = 25;
age = "25"; // 报错:不能将字符串赋值给 number 类型

3. 什么是联合类型?如何使用?

问题: 定义一个函数参数可以是 stringnumber

答案:
使用 | 表示联合类型。

function printId(id: number | string): void {
    console.log(`ID: ${id}`);
}

printId(101);      // OK
printId("101");    // OK
printId(true);     // 报错

4. 什么是类型断言?如何使用?

问题: 告诉编译器某个值的具体类型。

答案:
使用 <Type>as Type

const input = document.getElementById("username") as HTMLInputElement;
input.value = "默认值";

5. 接口(interface)和类型别名(type)的区别?

问题: interface Person {} vs type Person = {} 的区别。

答案:

特性interfacetype
可扩展性支持声明合并不支持
支持继承支持 extends支持交叉类型 &
支持基本类型✅(如 `type ID = string
使用场景对象结构建模复杂类型组合
// interface
interface User {
    name: string;
}

// type
type ID = string | number;

6. 如何定义可选属性?

问题: 在接口中定义一个可选的 age 属性。

答案:
使用 ? 标记可选属性。

interface Person {
    name: string;
    age?: number; // 可选
}

const p: Person = { name: "Alice" }; // 合法

7. 元组(Tuple)的作用是什么?如何定义?

问题: 创建一个元组表示 [姓名, 年龄]

答案:
元组允许你定义数组中元素的类型顺序。

let user: [string, number];
user = ["Alice", 25]; // 合法
user = [25, "Alice"]; // 报错:类型顺序不符

8. 枚举(enum)有什么用途?如何定义?

问题: 定义一个表示星期的枚举。

答案:
枚举用于命名一组常量值。

enum WeekDay {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday
}

console.log(WeekDay.Monday); // 输出: 0
console.log(WeekDay[3]);     // 输出: "Thursday"

9. 泛型(Generic)的作用是什么?如何使用?

问题: 写一个通用的 identity 函数,返回传入的任意类型。

答案:
泛型让函数/类/接口具有更强的灵活性和复用性。

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

console.log(identity<string>("hello")); // 输出: "hello"
console.log(identity<number>(123));     // 输出: 123

10. 类型推断(Type Inference)是什么?

问题: 如果没有显式标注类型,TS 如何确定类型?

答案:
TypeScript 会根据赋值自动推断类型。

let message = "Hello"; // 类型自动推断为 string
message = 123;         // 报错:不能将 number 赋值给 string

11. 如何定义函数类型?

问题: 定义一个函数类型,接受两个数字,返回一个数字。

答案:
使用类型表达式 type Fn = (a: number, b: number) => number;

type Sum = (a: number, b: number) => number;

const add: Sum = (x, y) => x + y;
console.log(add(2, 3)); // 输出: 5

12. 类型守卫(Type Guard)是什么?如何实现?

问题: 判断一个值是否是数字类型。

答案:
使用 typeof 或自定义谓词函数。

function isNumber(x: any): x is number {
    return typeof x === "number";
}

function processValue(value: number | string): void {
    if (isNumber(value)) {
        console.log(value.toFixed(2));
    } else {
        console.log(value.toUpperCase());
    }
}

13. 如何定义一个类?

问题: 创建一个 Person 类,有 name和 sayHello() 方法。

答案:

class Person {
    name: string;

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

    sayHello(): void {
        console.log(`Hello, my name is ${this.name}`);
    }
}

const p = new Person("Tom");
p.sayHello(); // 输出: Hello, my name is Tom

14. 类中的访问修饰符有哪些?分别代表什么含义?

问题: publicprivateprotected 的区别?

答案:

  • public:任何地方都可以访问(默认)。
  • private:只能在类内部访问。
  • protected:可在类及子类中访问。
class Animal {
    public name: string;
    private secret: string;
    protected family: string;

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

class Dog extends Animal {
    showFamily() {
        console.log(this.family); // 合法
        // console.log(this.secret); // ILLEGAL
    }
}

15. 如何实现接口继承?

问题:Employee 接口继承 Person 接口。

答案:
使用 extends 实现接口继承。

interface Person {
    name: string;
}

interface Employee extends Person {
    id: number;
}

const emp: Employee = { name: "John", id: 1 };

16. 如何定义只读属性?

问题: 定义一个不可修改的 id 字段。

答案:
使用 readonly 关键字。

interface User {
    readonly id: number;
    name: string;
}

const user: User = { id: 1, name: "Alice" };
user.id = 2; // 报错:无法修改只读属性

17. 装饰器(Decorator)是什么?如何使用?

问题: 写一个简单的类装饰器。

答案:
装饰器是一种特殊类型的声明,可用于类、方法、属性等。

function log(target: any, key: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Calling "${key}" with`, args);
        return originalMethod.apply(this, args);
    };
}

class Calculator {
    @log
    add(a: number, b: number): number {
        return a + b;
    }
}

const calc = new Calculator();
calc.add(2, 3);
// 输出: Calling "add" with [2, 3]

18. anyunknown 的区别?

问题: anyunknown 在类型检查时有何不同?

答案:

  • any:绕过所有类型检查。
  • unknown:必须先做类型检查后才能操作。
let value: unknown;
value = "hello";
if (typeof value === "string") {
    console.log(value.toUpperCase()); // 安全地使用
}

let val: any = 123;
val = "abc";
val = true;
console.log(val.foo.bar.baz); // 不会报错(但运行时报错)

19. 如何定义可索引类型?

问题: 创建一个对象,通过字符串索引获取值。

答案:
使用索引签名。

interface StringArray {
    [index: number]: string;
}

const arr: StringArray = ["Apple", "Banana"];
console.log(arr[0]); // 输出: "Apple"

interface Dictionary {
    [key: string]: number;
}

const data: Dictionary = { one: 1, two: 2 };

20. 如何使用 never 类型?

问题: 写一个总是抛出错误的函数,并返回 never

答案:
never 表示永远不会返回值的函数。

function throwError(message: string): never {
    throw new Error(message);
}

function infiniteLoop(): never {
    while (true) {}
}

📋 总结表格

编号题目描述知识点示例代码常见考察点
1TS 与 JS 区别类型系统function greet(name: string)类型安全
2定义带类型的变量类型注解let age: number = 25基础语法
3联合类型`` 操作符`id: string
4类型断言强制类型转换as HTMLElement类型信任机制
5interface vs type类型定义方式interface A {} vs type A = {}接口设计
6可选属性? 修饰符age?: number数据灵活性
7元组类型固定长度数组[string, number]结构控制
8枚举类型枚举值映射enum Status { Success, Failed }可读性增强
9泛型参数化类型function identity<T>(value: T)通用性
10类型推断自动识别类型let msg = "hello"类型安全
11函数类型定义类型签名(a: number, b: number) => number函数编程
12类型守卫运行时判断typeof value === 'number'类型细化
13类定义OOP 语法class Person { ... }面向对象
14类成员访问控制public, private, protectedprivate secret: string封装原则
15接口继承扩展接口interface Employee extends Person接口设计
16只读属性readonlyreadonly id: number数据不变性
17装饰器应用类/方法增强@log()高阶拓展
18anyunknown 区别类型安全性value is unknown类型防御
19可索引类型索引签名{ [key: string]: number }动态对象
20never 的用途永远不返回function throwError(): never错误处理

### TypeScript 面试常见问题及答案 #### 1. TypeScript 的基本概念 TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的超集,添加了可选的静态类型和基于类的面向对象编程。TypeScript 终会被编译成纯 JavaScript 代码[^1]。 #### 2. 类型声明与类型推断的区别 类型声明是显式地为变量或函数指定类型,而类型推断是 TypeScript 根据赋值语句右侧的值自动推断变量的类型。例如: ```typescript // 类型声明 let x: number; x = 10; // 类型推断 let y = 20; // TypeScript会自动推断y的类型为number ``` 通过类型声明,开发者可以更明确地定义变量的类型,从而提高代码的可读性和安全性[^1]。 #### 3. 接口(Interface)的作用及使用场景 接口在 TypeScript 中用于定义对象的结构,包括属性和方法的名称、类型以及它们之间的关系。接口的主要作用是为类提供一个模板或者契约,确保类按照特定的方式实现。 接口的使用场景包括但不限于以下几种: - 定义对象的形状。 - 描述函数的参数和返回值。 - 作为类型检查器的参考,确保数据符合预期格式。 接口与类型别名(Type Alias)的区别在于: - 接口可以被扩展和重新打开以添加新的成员。 - 类型别名不能被扩展,但可以用来创建联合类型或元组类型[^1]。 #### 4. 类型断言的作用及注意事项 类型断言允许开发者手动指定一个值的类型。例如: ```typescript let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; ``` 需要注意的是,类型断言不会进行实际的类型检查,如果断言不正确,在运行时可能会导致错误[^3]。 #### 5. 模块的概念及其优势 TypeScript 模块提供了一种通过将代码封装到不同文件中来组织和重用代码的方法。模块可以导出和导入到其他文件中,从而更容易跨多个文件和项目重用代码。模块还有助于避免命名冲突并提高代码的可服务性和可扩展性[^4]。 #### 6. 全局类型声明的引入方式 `/// <reference types/>` 可用于引入一些全局的类型声明。例如,在 Web 项目中,可能需要引入 `@types/webpack-env` 来获取 `window`、`document` 等全局对象的类型声明。在项目的相关文件中添加 `/// <reference types="webpack-env" />`,就可以确保在代码中使用这些全局对象时,能够获得准确的类型检查[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈前端老曹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值