掌握TypeScript(TS)对于提升前端开发能力和求职竞争力至关重要,TS面试真题涵盖了从基础类型与接口到高级特性的各个方面。通过这些真题,你可以深入理解TS的特性和应用,从而在实际工作中编写更安全、更高效的代码。熟练掌握TS面试真题不仅能帮助你通过面试,还能提升你的编程水平。
TS面试真题简介
TS面试真题的重要性
在现代软件开发中,TypeScript(简称TS)因其静态类型检查和面向对象编程特性,被广泛应用于前端开发。掌握TypeScript不仅可以帮助开发者编写更安全、更健壮的代码,还能在求职过程中提升竞争力。因此,TS面试成为许多前端面试中不可或缺的一部分。通过熟练掌握TS面试题,你不仅能够提升自己对TS的理解,还能在实际工作中更好地利用其特性。
面试官常问的TS问题类型
- 基础知识:如变量类型、基本数据类型、接口等。
- 高级特性:如泛型、装饰器等。
- 面向对象编程:如类、继承、抽象类等。
- 类型检查与推断:如联合类型、交叉类型、类型推断等。
- 代码示例与实践:如如何编写高性能的TS代码、如何调试TS代码等。
基础类型与接口
常见的数据类型
TypeScript 支持多种数据类型,包括基本数据类型、复合数据类型和特殊类型。这些类型在日常开发中经常使用,掌握这些类型有助于编写更准确的代码。
-
基本数据类型:
- number:用于表示数值,可以是整数或浮点数。
- string:用于表示字符串。
- boolean:用于表示布尔值,只有
true
和false
。 - symbol:表示独一无二的值。
- undefined:表示未赋值的值。
- null:表示空值。
- 复合数据类型:
- 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中,接口用于定义对象的结构。接口定义了一组属性、方法和索引签名,但不实现它们。定义接口有助于确保对象符合预期的结构,从而提高代码的可读性和可维护性。
- 简单接口:定义对象的属性和方法。
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.`);
}
};
- 索引签名:定义对象的索引类型。
interface Indexable {
[index: string]: string;
}
let myObj: Indexable = {
a: "value1",
b: "value2"
};
- 可选属性:允许某些属性可选。
interface OptionalInterface {
name?: string;
age?: number;
}
let personOptional: OptionalInterface = {};
- 只读属性:确保对象的属性在初始化后不能被修改。
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.
- 方法重载:定义多个方法签名以实现方法重载。
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中,类是面向对象编程的核心概念之一。类允许你定义对象的结构和行为。类可以分为实例方法、静态方法、实例属性和静态属性。
- 实例方法和属性:
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!"
- 静态方法和属性:
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
关键字来实现。
- 继承和重写方法:
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!"
- 访问基类的方法和属性:
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"
- 抽象类:定义抽象类使用
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"
泛型与模板
泛型的基本概念
泛型是一种强大的编程技术,它允许你定义可复用的组件,这些组件可以处理多种类型的数据,而无需具体指定类型。泛型使用 <>
来定义类型参数。
- 简单泛型:
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"
- 泛型接口:
interface GenericIdentity<T> {
(arg: T): T;
}
let identityFunc: GenericIdentity<number> = (num: number) => num;
console.log(identityFunc(10)); // 输出 "10"
- 泛型类:
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"
泛型的应用场景
泛型在开发中有很多应用场景,包括但不限于:
- 数组操作:
- 使用泛型定义数组,确保数组中的元素类型一致。
let numbers: Array<number> = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];
- 函数返回类型:
- 使用泛型定义函数返回类型,确保返回值类型与传入参数类型一致。
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"
- 自定义集合类:
- 使用泛型定义自定义集合类,使其能够存储多种类型的数据。
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 提供了一些高级特性,可以进一步提高代码的灵活性和可维护性。这些特性包括:
- 装饰器:装饰器允许你为类或其成员添加额外的功能。
- 模板字符串:可以嵌入表达式和变量。
- 映射类型:可以对现有类型进行转换或修改。
- 类型保护:确保对类型进行更严格的检查。
- 重写继承的属性:可以覆盖基类中的属性。
如何在实践中应用这些特性
为了更好地应用这些特性,开发者需要理解其工作原理以及如何在实际项目中使用它们。以下是一些示例:
- 装饰器:
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();
- 模板字符串:
let name: string = "Alice";
let greeting: string = `Hello, ${name}!`;
console.log(greeting); // 输出 "Hello, Alice!"
- 映射类型:
interface OriginalType {
a: number;
b: string;
}
type MappedType = {
[P in keyof OriginalType]: OriginalType[P];
};
let mappedObj: MappedType = { a: 1, b: "value" };
- 类型保护:
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"
- 重写继承的属性:
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面试需要从多个方面入手,包括理论知识的掌握、实践经验的积累和面试技巧的培养。
-
理论知识:
- 掌握基础知识:了解TS的基本概念,如变量类型、函数、类、接口等。
- 深入学习高级特性:掌握泛型、装饰器、类型保护等高级特性。
- 阅读官方文档:熟悉TS的官方文档,了解最新的特性和最佳实践。
-
实践经验:
- 编写代码:通过编写实际代码来加深对TS的理解。
- 参与开源项目:参与开源项目可以提升你的实战能力。
- 练习面试题:多做面试题,熟悉面试中常见的问题和解决方法。
- 面试技巧:
- 准备常见问题:熟悉面试中常见问题的解答。
- 模拟面试:通过模拟面试来提升自己的表现。
- 保持自信:保持自信,从容应对面试中的各种问题。
面试中的常见技巧与注意事项
-
清晰表达:
- 在回答问题时,尽量使用清晰、简洁的语言,避免使用过于复杂的术语。
- 对于复杂的问题,可以分步骤来解释你的思路和答案。
-
逻辑严谨:
- 在回答问题时,保持逻辑严谨,确保回答的准确性。
- 避免在回答问题时出现逻辑上的漏洞。
-
积极参与:
- 在面试过程中,积极参与讨论,展示你的思考过程。
- 对于你不熟悉的问题,可以提出自己的理解和看法。
- 时间管理:
- 在回答问题时,注意时间管理,确保每个问题的回答时间适当。
- 避免在某个问题上花费过多时间,导致其他问题的回答时间不足。
面试真题实战演练
为了更好地准备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();
共同学习,写下你的评论
评论加载中...
作者其他优质文章