TypeScript面试题:新手必懂的基础知识与解答
本文详细介绍了TypeScript的核心特性和常见面试题的解析,帮助读者理解TypeScript的基础知识和应用场景。文中不仅讲解了类型推断、数组类型、函数类型、接口与类以及泛型与装饰器等关键概念,还提供了丰富的示例代码以加深理解。此外,文章还深入分析了一些典型的TypeScript面试题,确保读者能够掌握TypeScript面试中常见的知识点和解题思路。
TypeScript简介与特性TypeScript是JavaScript的一个超集,由微软开发,旨在为JavaScript提供静态类型检查和更强大的工具支持。TypeScript的设计原则是保持与JavaScript的兼容性,同时提供更强大的类型系统和其他功能,使得开发者能够编写出更安全、更易于维护的代码。
TypeScript的核心特性1. 静态类型检查
TypeScript引入了静态类型检查系统,允许开发者在代码编写阶段就发现潜在的类型错误,从而减少运行时的错误。开发者可以在变量、函数参数、返回值等地方定义类型,TypeScript编译器会在编译时检查这些类型是否一致,如果不一致,编译器会给出类型错误信息。
示例代码:
let age: number = 30;
age = "thirty"; // 编译错误,因为age定义为number类型,但此处试图赋值为字符串"thirty"
2. 类支持
TypeScript完全支持ES6的类特性,使得开发者能够更方便地进行面向对象编程。类支持继承、构造函数、静态成员等,使得代码组织更加清晰。
示例代码:
class Animal {
constructor(public name: string) {}
makeSound(): void {
console.log('Some generic sound');
}
}
class Dog extends Animal {
makeSound(): void {
console.log('Woof!');
}
}
const myDog = new Dog('Rex');
myDog.makeSound(); // 输出:Woof!
3. 接口定义
TypeScript的接口定义明确的类型结构,使得开发者能够定义复杂的对象结构、函数类型,并且确保这些类型在使用时的一致性。
示例代码:
interface Point {
x: number;
y: number;
}
let point: Point = { x: 10, y: 20 };
console.log(point.x, point.y); // 输出:10 20
4. 模块支持
TypeScript支持ES6模块系统,允许开发者将代码组织成模块,便于代码的复用和组织。开发者可以使用export
关键字导出模块中的内容,并使用import
关键字导入其他模块的内容。
示例代码:
// 文件:mathLib.ts
export function add(a: number, b: number) {
return a + b;
}
// 文件:main.ts
import { add } from './mathLib';
console.log(add(5, 3)); // 输出:8
5. 泛型
TypeScript的泛型使得函数、类或接口能够处理多种类型的数据,而无需指定具体的类型。这使得代码更加灵活和通用。
示例代码:
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>('Hello, world!'); // 把类型参数声明为字符串
console.log(output); // 输出:Hello, world!
6. 装饰器
TypeScript支持ES7的装饰器特性,允许开发者在类、方法或属性上定义装饰器,从而在不修改原有代码的情况下,增强代码功能或进行AOP(面向切面编程)。
示例代码:
function log(target: any, name: string | symbol) {
console.log(`Property accessed: ${name}`);
}
class User {
@log
name: string = 'John Doe';
}
const user = new User();
user.name; // 输出:Property accessed: name
基础类型详解
TypeScript中的类型分为基础类型和复杂类型两大类。基础类型是最常见的类型,包括数字、字符串、布尔值等。复杂类型则包括数组、元组等更为复杂的类型结构。
基本类型1. number类型
number
类型用于表示数字,可以是整数或浮点数。
示例代码:
let age: number = 30;
age = 30.5; // 正确,浮点数也是number类型
age = 'thirty'; // 编译错误,因为age定义为number类型
2. string类型
string
类型用于表示文本字符串。
示例代码:
let message: string = 'Hello, World!';
message = '你好,世界!'; // 正确,支持中文
message = 123; // 编译错误,因为message定义为string类型
3. boolean类型
boolean
类型用于表示布尔值,值可以是true
或false
。
示例代码:
let isCompleted: boolean = true;
isCompleted = 1; // 编译错误,因为isCompleted定义为boolean类型
4. void类型
void
类型用于表示函数没有返回值。
示例代码:
function greet(): void {
console.log('Hello!');
}
let result: void = greet(); // result的类型为void
5. null和undefined类型
null
和undefined
类型用于表示空值和未定义值。
示例代码:
let empty: null = null;
let notDefined: undefined = undefined;
6. any类型
any
类型用于表示任何类型的值。如果不确定变量的类型,可以将类型声明为any
,但使用any
类型会失去类型检查的好处。
示例代码:
let age: any = 30;
age = 'thirty'; // 正确,因为age类型为any
复杂类型
1. 数组类型
数组类型用于表示固定类型的数据集合,可以通过[]
操作符声明数组类型。
示例代码:
let numbers: number[] = [1, 2, 3];
numbers.push(4); // 正确,因为numbers是一个number类型的数组
numbers.push('four'); // 编译错误,因为numbers是一个number类型的数组
2. 元组类型
元组类型用于表示固定长度的数组,每个位置可以有不同的类型。
示例代码:
let person: [string, number] = ['John', 30];
person.push('Engineer'); // 编译错误,因为元组的长度固定为2
console.log(person[0], person[1]); // 输出:John 30
3. 对象类型
对象类型用于表示具有固定属性的对象,可以通过对象字面量声明对象类型。
示例代码:
interface User {
name: string;
age: number;
}
let user: User = { name: 'John Doe', age: 30 };
user = { name: 'Jane Doe', age: 25, country: 'China' }; // 编译错误,因为user对象多了country属性
4. 类型别名
类型别名用于为现有类型定义一个新名称。
示例代码:
type Point = {
x: number;
y: number;
};
let p: Point = { x: 10, y: 20 };
函数与接口
TypeScript中的函数和接口是两个重要的概念,它们有助于定义函数的行为和结构,确保代码的一致性和可维护性。
函数定义在TypeScript中,可以定义函数的参数类型和返回值类型,以确保函数的输入和输出类型的一致性。
示例代码:
function add(a: number, b: number): number {
return a + b;
}
let result: number = add(4, 5); // 调用函数并获取返回值
console.log(result); // 输出:9
函数的参数可以是可选的、可重复的,或者有默认值。
示例代码:
function add(a: number, b?: number): number {
return a + (b || 0);
}
let result1: number = add(4); // 只传递第一个参数
let result2: number = add(4, 5); // 传递两个参数
console.log(result1, result2); // 输出:4 9
1. 可选参数
可选参数允许函数在调用时省略某些参数。
示例代码:
function greet(name: string, message?: string) {
if (message) {
console.log(`${name}, ${message}`);
} else {
console.log(`Hello, ${name}`);
}
}
greet('Alice'); // 输出:Hello, Alice
greet('Bob', 'Good morning'); // 输出:Bob, Good morning
2. 默认参数
默认参数允许在没有提供实际参数值时使用默认值。
示例代码:
function greet(name: string, message: string = 'Hello') {
console.log(`${message}, ${name}`);
}
greet('Alice'); // 输出:Hello, Alice
greet('Bob', 'Good morning'); // 输出:Good morning, Bob
3. 可变参数
可变参数允许在函数调用时传递不同数量的参数。
示例代码:
function add(...numbers: number[]): number {
return numbers.reduce((sum, current) => sum + current, 0);
}
let result: number = add(1, 2, 3, 4); // 使用可变参数
console.log(result); // 输出:10
接口使用
接口在TypeScript中用于定义对象的结构,确保对象具有特定的属性和方法。接口可以用于函数参数、类成员等。
示例代码:
interface Point {
x: number;
y: number;
}
function printPoint(p: Point) {
console.log(`Point (${p.x}, ${p.y})`);
}
let point: Point = { x: 10, y: 20 };
printPoint(point); // 输出:Point (10, 20)
接口还可以定义方法,确保对象具有特定的行为。
示例代码:
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
class BasicCalculator implements Calculator {
add(a: number, b: number): number {
return a + b;
}
subtract(a: number, b: number): number {
return a - b;
}
}
let calc: Calculator = new BasicCalculator();
console.log(calc.add(4, 5)); // 输出:9
console.log(calc.subtract(4, 5)); // 输出:-1
类与继承
TypeScript中的类支持面向对象编程的核心特性,包括构造函数、属性、方法、静态成员和继承。
类的定义与使用类是面向对象编程的基础,它允许开发者定义具有特定属性和方法的对象类型。
示例代码:
class Animal {
constructor(public name: string) {}
makeSound(): void {
console.log('Some generic sound');
}
}
let animal: Animal = new Animal('Generic Animal');
animal.makeSound(); // 输出:Some generic sound
在类中,可以定义公共(public)、私有(private)、受保护(protected)成员,以控制成员的访问权限。
示例代码:
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
bark(): void {
console.log(`${this.name} barks`);
}
}
let dog: Dog = new Dog('Rex');
dog.makeSound(); // 输出:Rex makes a sound
dog.bark(); // 输出:Rex barks
在类中,可以定义静态成员,这些成员属于类本身,而不是类的实例。
示例代码:
class Animal {
static numberOfAnimals: number = 0;
constructor() {
Animal.numberOfAnimals++;
}
}
let animal1: Animal = new Animal();
let animal2: Animal = new Animal();
console.log(Animal.numberOfAnimals); // 输出:2
1. 构造函数
构造函数是类的一个特殊方法,用于初始化新创建的对象实例。
示例代码:
class Point {
constructor(public x: number, public y: number) {}
toString(): string {
return `(${this.x}, ${this.y})`;
}
}
let origin: Point = new Point(0, 0);
console.log(origin.toString()); // 输出:(0, 0)
2. 静态成员
静态成员属于类本身,而不是类的实例,可以通过类名直接访问。
示例代码:
class MathUtils {
static PI: number = 3.14;
static square(x: number): number {
return x * x;
}
}
console.log(MathUtils.PI); // 输出:3.14
console.log(MathUtils.square(5)); // 输出:25
继承的概念与应用
继承允许一个类继承另一个类的属性和方法,从而实现代码复用和层次化结构。
示例代码:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
bark(): void {
console.log(`${this.name} barks`);
}
}
let dog: Dog = new Dog('Rex');
dog.makeSound(); // 输出:Rex makes a sound
dog.bark(); // 输出:Rex barks
在继承中,子类可以重写父类的方法,以提供特定的行为。
示例代码:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
console.log(`${this.name} barks`);
}
}
let dog: Dog = new Dog('Rex');
dog.makeSound(); // 输出:Rex barks
泛型与装饰器
泛型和装饰器是TypeScript中较为高级的概念,它们提供了更灵活和强大的编程能力。
泛型基本概念泛型允许定义函数、类或接口时指定类型的参数,使得代码更加通用和灵活。
示例代码:
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>('Hello, world!'); // 把类型参数声明为字符串
console.log(output); // 输出:Hello, world!
泛型可以用于类和接口中,以表示泛型类型。
示例代码:
class Collection<T> {
items: T[] = [];
add(item: T): void {
this.items.push(item);
}
get(index: number): T {
return this.items[index];
}
}
let numbers: Collection<number> = new Collection<number>();
numbers.add(1);
numbers.add(2);
console.log(numbers.get(0)); // 输出:1
装饰器使用入门
装饰器是一种元编程工具,允许在类、方法或属性上定义装饰器,从而在不修改原有代码的情况下,增强代码功能或进行AOP编程。
示例代码:
function log(target: any, name: string | symbol) {
console.log(`Property accessed: ${name}`);
}
class User {
@log
name: string = 'John Doe';
}
const user = new User();
user.name; // 输出:Property accessed: name
装饰器可以用于方法的定义,以实现特定的行为增强。
示例代码:
function readonly(target: any, name: string, descriptor: PropertyDescriptor) {
descriptor.writable = false;
return descriptor;
}
class User {
@readonly
name: string = 'John Doe';
getName(): string {
return this.name;
}
}
let user = new User();
console.log(user.getName()); // 输出:John Doe
user.name = 'Jane Doe'; // 报错,因为name属性已设置为只读
实战:TypeScript面试题解析
在面试中,了解TypeScript的基础知识是必要的。下面是一些常见的面试题类型及其示例代码解析。
常见面试题类型1. 类型推断
在TypeScript中,编译器可以根据变量的初始值推断出变量的类型。
示例代码:
let age = 30; // 编译器推断age为number类型
let message = 'Hello, world!'; // 编译器推断message为string类型
2. 数组类型
理解数组类型的定义和使用。
示例代码:
let numbers: number[] = [1, 2, 3];
numbers.push(4); // 正确,因为numbers是一个number类型的数组
3. 函数类型
理解函数的参数类型和返回值类型。
示例代码:
function add(a: number, b: number): number {
return a + b;
}
let result: number = add(4, 5); // 调用函数并获取返回值
4. 接口与类
理解接口和类的定义、继承和实现。
示例代码:
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} barks`);
}
}
let dog: Dog = new Dog('Rex');
dog.makeSound(); // 输出:Rex barks
5. 泛型与装饰器
理解泛型和装饰器的基本概念及其使用。
示例代码:
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>('Hello, world!'); // 把类型参数声明为字符串
console.log(output); // 输出:Hello, world!
function log(target: any, name: string | symbol) {
console.log(`Property accessed: ${name}`);
}
class User {
@log
name: string = 'John Doe';
}
const user = new User();
user.name; // 输出:Property accessed: name
示例代码解析
1. 类型推断
let age = 30; // 编译器推断age为number类型
let message = 'Hello, world!'; // 编译器推断message为string类型
这里,编译器根据变量的初始值自动推断出变量的类型。对于age
,初始值为30
,因此age
被推断为number
类型。对于message
,初始值为字符串'Hello, world!'
,因此message
被推断为string
类型。
2. 数组类型
let numbers: number[] = [1, 2, 3];
numbers.push(4); // 正确,因为numbers是一个number类型的数组
这里,numbers
是一个number
类型的数组。push
方法用于向数组中添加元素。由于numbers
是一个number
类型的数组,因此只能添加number
类型的元素。这里添加4
是正确的,但添加字符串或其他类型会引发编译错误。
3. 函数类型
function add(a: number, b: number): number {
return a + b;
}
let result: number = add(4, 5); // 调用函数并获取返回值
这里,add
函数接受两个number
类型的参数,并返回一个number
类型的值。result
变量用于保存add
函数的返回值,其类型为number
。
4. 接口与类
interface Animal {
name: string;
makeSound(): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log(`${this.name} barks`);
}
}
let dog: Dog = new Dog('Rex');
dog.makeSound(); // 输出:Rex barks
这里,Animal
接口定义了一个name
属性和一个makeSound
方法。Dog
类实现了Animal
接口,包含一个name
属性和一个makeSound
方法。创建一个Dog
实例并调用makeSound
方法,输出Rex barks
。
5. 泛型与装饰器
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>('Hello, world!'); // 把类型参数声明为字符串
console.log(output); // 输出:Hello, world!
function log(target: any, name: string | symbol) {
console.log(`Property accessed: ${name}`);
}
class User {
@log
name: string = 'John Doe';
}
const user = new User();
user.name; // 输出:Property accessed: name
这里,identity
函数是一个泛型函数,接受一个类型参数T
,返回一个T
类型的值。output
变量调用identity
函数时,类型参数T
被指定为string
,因此output
的类型为string
。
log
函数是一个装饰器,用于在属性访问时打印属性名。User
类中的name
属性被log
装饰器装饰,当访问name
属性时,会打印出Property accessed: name
。
以上是TypeScript面试题的一些常见类型及其示例代码解析,通过这些例子,希望你能更好地理解和掌握TypeScript的基础知识。
共同学习,写下你的评论
评论加载中...
作者其他优质文章