什么是TypeScript?与JavaScript有何区别?常考面试题有哪些?
- 什么是TypeScript?
- TypeScript与JavaScript的区别
- 有关TypeScript的常考面试题
- 1. TypeScript是什么?它与JavaScript有何不同?
- 2. 什么是TypeScript的类型系统?为什么要使用它?
- 3. 如何定义变量的类型/如何使用类型注解?
- 4. 什么是接口(Interfaces)?有什么作用?interface和type有什么区别?
- 5. TypeScript中的类和继承是如何工作的?
- 6. TypeScript中的枚举(Enums)是什么?如何使用?
- 7. TypeScript中的泛型(Generics)是什么?请举例说明其用途。
- 8. TypeScript中的模块(Modules)是什么?如何导入和导出模块?
- 9. 什么是命名空间(Namespaces)?它们在TypeScript中的作用是什么?
- 10. TypeScript中的类型推断是如何工作的?
- 11. 什么是类型别名(Type Aliases)?
- 12. TypeScript中的类型保护是什么?
- 13. TypeScript中的类型断言是什么?
- 14. TypeScript中的可选参数和默认参数是什么?
- 15. 如何处理在TypeScript中的空值和未定义值?
什么是TypeScript?
TypeScript是JavaScript 的超集。这意味着 TypeScript 包含了 JavaScript 的所有功能,并且在此基础上增加了静态类型系统和其他一些新特性。
关键词:类型
TypeScript与JavaScript的区别
TypeScript | JavaScript | 优点/缺点 | |
---|---|---|---|
静态类型系统 | 可为变量、参数、返回值等明确指定类型 | - | 优点:有助于在编码阶段捕获错误,并提供更好的代码编辑、自动补全和重构支持。 |
类型注解 | 可通过类型注解来指定变量的类型 | - | 优点:有更多的可读性和明确性,有助于他人理解代码 |
编译过程 | 通过编译器编译成 JavaScript (类型检查等工作 -> JavaScript 代码) | 解释型语言,动态编译 | 缺点:需要额外的编译步骤 |
类和接口 | 更丰富的类和接口支持 | 支持面向对象编程(OOP)* | 优点:能够更好地利用类和借口进行类型检查和提示 |
总结:TypeScript 可以看作是 JavaScript 的增强版,它提供了更严格的类型检查和更丰富的工具来帮助开发人员编写可维护、可扩展的代码。尽管它需要额外的编译步骤,但在大型项目和团队中,TypeScript 的静态类型系统可以大大提高代码质量和开发效率。
有关TypeScript的常考面试题
1. TypeScript是什么?它与JavaScript有何不同?
TypeScript是一种由Microsoft开发的开源编程语言,它是JavaScript的超集。与JavaScript相比,TypeScript提供了静态类型系统、更严格的编译检查、更丰富的面向对象编程支持和更好的工具链*。(详见本文第一,第二部分)
*如何理解“更好的工具链”?
"更好的工具链"指的是 TypeScript 在开发过程中提供的一系列工具和生态系统,包括编辑器、IDE、调试器、构建工具等,以及与这些工具集成的插件和扩展。这些工具能够提高开发效率、代码质量和开发体验,让开发者更轻松地编写、调试和维护 TypeScript 代码。
2. 什么是TypeScript的类型系统?为什么要使用它?
TypeScript的类型系统允许开发人员在编码时定义变量、参数、函数返回值等的类型。它提供了类型检查功能,在编码阶段捕获错误,提高代码的可读性、可维护性和可靠性。类型系统可以减少因类型错误导致的运行时错误,并提供更好的代码编辑和重构支持。
3. 如何定义变量的类型/如何使用类型注解?
// 使用类型注解定义变量类型
let myNumber: number = 10;
let myString: string = "Hello";
let myBoolean: boolean = true;
// 使用类型注解定义函数参数和返回值类型
function add(x: number, y: number): number {
return x + y;
}
4. 什么是接口(Interfaces)?有什么作用?interface和type有什么区别?
接口是一系列抽象方法的声明,是一些方法特征(属性和方法)的集合。
这些方法都应该是抽象的(提高代码的可读性和可维护性,能够在编译阶段检测出对象结构的不匹配),需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
接口(interfaces)和类型别名(type aliases)(简称 type)都用于定义自定义类型,但它们有一些不同之处:
接口(Interfaces):
- 接口用于定义对象的结构和行为,可以描述对象的属性、方法、函数签名等。
- 接口可以被类(class)实现(implements),从而实现接口的约束。
- 接口可以被扩展(extend)。
类型别名(Type Aliases):
- 类型别名可以为任意类型创建别名,包括原始类型、联合类型、交叉类型、元组、函数等。
- 类型别名可以用于复杂类型的简化和重用。
- 类型别名不能被扩展或实现。
总结:接口更适合用于描述对象的结构和行为,而类型别名更适合用于创建复杂类型别名或简化已有类型的别名。
5. TypeScript中的类和继承是如何工作的?
TypeScript中的类和继承与其他面向对象语言类似:
class
: 定义类(可以包含属性和方法);extends
: 继承父类;implement
: 实现接口;- 访问修饰符(
public
、private
、protected
): 控制成员的可访问性
什么时候用public/ private/ protected?
- private: 只能在类的内部访问。通常用于隐藏实现细节,保护数据的完整性。一般类中的属性均设置为private,如果需要读取/更新,可设置额外的getter和setter。
- protected: 类的内部和子类中可以访问,外部不能访问。如果外部类不会用到此方法,但是子类会用到的话,用protected。
- public(默认): 在类的内部和外部都可以访问。当希望成员在类的外部可以被访问时,使用 public。
6. TypeScript中的枚举(Enums)是什么?如何使用?
枚举是一种用来描述一组命名常量集合的数据类型。在TypeScript中,可以使用enum关键字定义枚举,枚举成员可以是数字、字符串或表达式。枚举提供了更好的可读性和类型安全性。
// eg. 1
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
// eg. 2
enum Direction {
Up = 1, // 1
Down, // 2
Left = 10, // 10
Right, // 11
}
// eg. 3
enum Direction {
Up = "UP", // "UP"
Down = "DOWN", // "DOWN"
Left = "LEFT", // "LEFT"
Right = "RIGHT",// "RIGHT"
}
7. TypeScript中的泛型(Generics)是什么?请举例说明其用途。
泛型是一种用来创建可重用的组件的工具,它可以在不同类型之间共享代码。在TypeScript中,可以使用泛型来创建通用的函数、类和接口来增强代码的可重用性和灵活性, 避免重复的类型转换和代码重复。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("Hello TypeScript"); // 指定泛型参数为字符串类型
console.log(output); // 输出:Hello TypeScript
let output2 = identity<number>(42); // 指定泛型参数为数字类型
console.log(output2); // 输出:42
8. TypeScript中的模块(Modules)是什么?如何导入和导出模块?
模块是一种用来组织代码的方式,可以将代码分割成多个文件,并且提供了封装和复用的机制。在TypeScript中,可以使用import和export关键字来导入和导出模块。
// module1.ts
export const PI = 3.14;
// module2.ts
import { PI } from './module1';
9. 什么是命名空间(Namespaces)?它们在TypeScript中的作用是什么?
命名空间是一种用来组织代码的方式,可以防止全局命名冲突。在TypeScript中,可以使用namespace关键字定义命名空间,并且使用export关键字导出其中的成员。命名空间提供了更好的模块化和封装性。
namespace Geometry {
export interface Point {
x: number;
y: number;
}
export function distance(p1: Point, p2: Point): number {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
}
let point1: Geometry.Point = { x: 0, y: 0 };
let point2: Geometry.Point = { x: 3, y: 4 };
console.log(Geometry.distance(point1, point2)); // 输出:5
10. TypeScript中的类型推断是如何工作的?
类型推断是TypeScript编译器根据变量的初始化值自动推断其类型的过程。当变量声明时未显式指定类型时,编译器会根据变量的使用上下文来推断其类型。或基于返回值类型:当函数有返回值时,TypeScript 编译器会根据函数返回值的类型推断出函数的返回类型。
11. 什么是类型别名(Type Aliases)?
类型别名(Type Aliases)是 TypeScript 中的一种机制,允许为已有类型定义一个新的名称。使用类型别名可以简化复杂类型的书写,并且提高了代码的可读性和可维护性。
// 定义类型别名
type Point = {
x: number;
y: number;
};
// 使用类型别名
let p: Point = { x: 10, y: 20 };
12. TypeScript中的类型保护是什么?
类型保护是一种在 TypeScript 中用于判断变量的类型的技术,可以在编译时对变量的类型进行检查,从而提高代码的类型安全性。常见的类型保护方法包括类型断言、类型谓词和使用类型判断函数。
// 使用类型谓词进行类型保护
function isNumber(x: any): x is number {
return typeof x === "number";
}
let value: string | number = 42;
if (isNumber(value)) {
// 在类型为 number 的分支中,value 的类型被收窄为 number
console.log(value.toFixed(2)); // 输出:42.00
}
13. TypeScript中的类型断言是什么?
类型断言是一种在 TypeScript 中告诉编译器一个值的类型的方式,它类似于类型转换,但不会进行类型检查或者数据的转换,而是告诉编译器相信开发者的判断。类型断言可以在需要时手动指定值的类型,通常用于解决类型推断不准确的问题或者在编写复杂类型时提高可读性。
// 使用尖括号语法进行类型断言
let value: any = "hello";
let length: number = (<string>value).length;
// 使用 as 关键字进行类型断言
let value: any = "hello";
let length: number = (value as string).length;
14. TypeScript中的可选参数和默认参数是什么?
可选参数(Optional Parameters): 可以在函数声明时通过在参数后面加上问号 ? 来表示可选参数,调用函数时可以不传入该参数。如果没有传入可选参数,则该参数的值为 undefined。
function greet(name?: string) {
if (name) {
console.log("Hello, " + name);
} else {
console.log("Hello, guest");
}
}
greet(); // 输出:Hello, guest
greet("John"); // 输出:Hello, John
默认参数(Default Parameters): 可以在函数声明时通过给参数赋值来指定默认参数值。如果调用函数时没有传入该参数,则使用默认参数值。
function greet(name: string = "guest") {
console.log("Hello, " + name);
}
greet(); // 输出:Hello, guest
greet("John"); // 输出:Hello, John
15. 如何处理在TypeScript中的空值和未定义值?
可以通过类型检查和使用类型保护来处理null
空值和undefined
未定义值。检查变量是否为这些特定值。
let value: string | null | undefined = "hello";
if (value === null || value === undefined) {
console.log("Value is null or undefined");
} else {
console.log("Value is " + value);
}
可以使用类型断言将变量转换为非空值类型,或者使用非空断言操作符 !
来告诉编译器该变量不会为空值或未定义值。
let value: string | null | undefined = "hello";
let length: number = value!.length; // 使用非空断言操作符
console.log(length); // 输出:5
总结:在处理空值和未定义值时,建议使用类型检查、类型保护和类型断言等技术来保证代码的类型安全性和正确性。