什么是 TypeScript
TypeScript是一种由微软开发的开源编程语言,它是JavaScript的超集。TypeScript通过添加静态类型、类、接口和模块等功能,使得在大型应用程序中更容易进行维护和扩展。它可以被编译为纯JavaScript,从而能够在任何支持JavaScript的地方运行。使用TypeScript可以帮助开发人员在编码过程中避免一些常见的错误,并提供更好的代码编辑功能和工具支持。
类型声明和类型推断的区别,并举例
类型声明是显式地为变量或者函数指定类型,而类型推断是 TypeScript 根据赋值语句右侧的值自动推断变量类型。
1 | // 类型声明 |
什么是接口(interface),作用和使用场景,接口和类型别名(Type Alias)的区别
interface
- 定义对象类型,主要用于定义对象的形状,描述对象应该具有哪些属性和方法
1
2
3
4interface Person {
name: string;
age: number;
} - 可扩展性,interface 可以被扩展,可以通过继承来添加更多的属性和方法
1
2
3
4
5
6
7interface Person {
name: string;
age: number;
}
interface Student extends Person {
gender: number;
} - 实现接口,以确保类具有接口中定义的所有属性和方法
1
2
3
4
5
6
7
8class Employee implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
type
- 定义类型别名,可以用于定义任何类型的别名,包括基本类型、联合类型、交叉类型等。
1
2// 表示一个可以是字符串或数字的类型
type StringOrNumber = string | number; - 不能被扩展。
- 用于复杂类型,如函数类型、元组类型等。
1
2// 表示一个接受一个数字参数并返回 void 的函数
type Callback = (arg: number) => void;
interface 用于描述对象的形状,描述对象应该具有的属性和方法。在 TypeScript 中,interface 可以用来约束对象的结构,以提高代码的可读性和维护性。
总结:
- interface 和 type 在很多情况下可以相互替代,但在一些特定场景下,选择其中一个可能更加合适
- 如果主要定义对象的形状,并且可能需要扩展,那么 interface 是一个好的选择
- 如果需要定义复杂的类型别名,或者不需要扩展的类型,那么 type 是一个好的选择
什么是泛型(generics),作用和使用场景
泛型是一种参数化类型,它可以在定义时不确定具体的类型,而是在使用时才确定具体的类型。泛型可以用于函数、类、接口等,以提高代码的复用性和灵活性。例如
1 | function identity<T>(arg: T): T { |
什么是枚举(enum),作用和使用场景,枚举和常量枚举的区别
枚举(enum)是一种用于定义一组命名常量的类型,它可以提高代码的可读性和维护性。枚举是一种类型,它允许你定义一组命名常量,枚举中的每个成员都有一个名称和一个值:
1 | enum Color { |
在这个例子中,定义了一个名为 Color 的枚举,它包含了三个成员:Red
、Green
和 Blue
。默认情况下,枚举成员的值从 0 开始递增,所以它们的值分别为 0、1 和 2。可以使用枚举成员来访问枚举值,例如:
1 | let c: Color = Color.Red; |
手动分配枚举值:
1 | enum Color { |
常量枚举(const enum),它是在编译阶段被完全移除的枚举,不会在生成的 JavaScript 代码中出现,而是在编译阶段被替换为相应的常量值。
1 | const enum Color { |
在这个例子中,定义了一个常量枚举 Color
,在编译后的 JavaScript 代码中, color 变量将被直接赋值为 0,而不会有 Color
枚举的存在。
优点:
- 常量枚举可以减少生成的 JavaScript 代码的大小,因为它们在编译时被替换它们的值
- 它们也可以提高代码的性能,因为在运行时不需要进行枚举的查找
枚举和常量枚举:
枚举
可以包含计算得出的值,而常量枚举则在编译阶段被删除,并且不能包含计算得出的值,只能包含常量成员常量枚举
在编译后会被删除,而普通枚举会生成真实的对象。
介绍 TypeScript 的可选属性、只读属性和类型断言
- 可选属性,使用
?
来标记一个属性可以存在,也可以不存在。 - 只读属性,使用
readonly
来标记一个属性只能在声明时赋值,不能在后续的代码中修改。 - 类型断言,使用
as
关键字来告诉编译器一个变量的类型。
使用场景:- 当你不确定一个变量的类型时,可以使用类型断言来告诉编译器。
1
2let str: any = "hello";
let len: number = (str as string).length; // 类型断言 - 当你需要将一个联合类型的值指定为其中的某一个类型时。
1
2let x: number | string = "hello";
let str: string = x as string; // 类型断言
- 当你不确定一个变量的类型时,可以使用类型断言来告诉编译器。
类型断言只是一种告诉编译器你认为某个值的类型的方式,但是它并不能真正改变值的类型,如果断言不正确,在运行时将导致错误。
1 | // 可选属性 |
联合类型(Union Types)
允许一个变量可以是多种类型中的一种,使用 |
来分隔不同的类型。可以让代码更加灵活和可维护,同时也可以提高类型安全性。
1 | // value 可以是 string 类型或者 number 类型 |
交叉类型(Intersection Types)
用于将多个类型合并为一个类型,通过使用 &
符号实现。
1 | type Person = { name: string; age: number }; |
接口也可以交叉:
1 | interface Shape { |
注意事项:
- 如果交叉的类型中有同名的属性,那么这些属性的类型必须是兼容的
- 交叉类型可以创建非常复杂的类型,但也可能导致代码的可读性降低,在使用时要确保代码的意图清晰,避免过度复杂的类型定义
索引类型(Index Types)
使用场景:
- 处理动态属性名
1
2
3
4
5
6
7
8
9
10
11interface Config {
[key: string]: string | number | boolean;
}
const config: Config = {
url: 'xxx',
enableLog: true,
timeout: 5000
}
function getConfig(key: string): string | number | boolean {
return config[key]
} - 处理 API 响应数据,当从 API 接收到的数据结构不确定时,可以使用索引类型来处理这些数据
1
2
3
4
5
6
7
8interface ApiResponse {
[key: string]: any;
}
const response: ApiResponse = {
name: "John",
age: 30,
salary: 5000
}; - 创建可扩展的对象类型(可以动态添加属性的对象类型)
1
2
3
4
5
6type ExtensibleObject = {
[key: string]: any;
};
const obj: ExtensibleObject = {};
obj.a = 'test'
obj.b = 123
const 和 readonly 关键字
const
- 用于变量声明,const 用于声明一个常量,它的值不能被重新赋值
- 基本类型和引用类型的区别
- 基本类型:const 声明的基本类型变量的值是不可变的,即不能被重新赋值
- 引用类型:const 声明的引用类型变量的引用是不可变的,即不能被重新赋值,但引用类型的属性或元素可以被修改
readonly
- 用于属性声明,readonly 用于声明类的属性或接口的属性为只读属性,即只能在对象初始化时或在构造函数中赋值,之后不能被重新赋值
- 数组和对象的只读性,当 readonly 用于数组或对象类型时,数组或对象本身的引用可以被重新赋值,但它们的元素或属性不能被修改(如果它们不是本身也被 readonly 修饰)
使用场景:
- const
- 当你确定一个变量的值在整个程序执行过程中都不会改变时,使用 const 声明变量。
- 对于简单的值类型(如数字、字符串、布尔值)和不可变的引用类型(如不可变的对象或数组),使用 const 可以提高代码的可读性和安全性。
- readonly
- 在类中,当你希望某个属性在对象创建后不能被修改时,使用 readonly 修饰属性。
- 在接口中,当你希望某个属性在实现该接口的对象中是只读的时,使用 readonly 修饰属性。
- 对于需要在多个地方共享但不希望被修改的对象或数组,可以使用 readonly 修饰它们的类型,以确保它们的内容不会被意外修改。
any 类型
尽管any类型提供了灵活性,但由于它会放弃TypeScript的静态类型检查,因此滥用any类型可能会降低代码的健壮性和可维护性。当滥用any类型时,可能会导致以下后果:
- 代码可读性下降
- 潜在的运行时错误
- 类型安全受损
数据类型
- 基本类型
- number,表示数字,包含证书和浮点数
- string,表示文本字符串
- boolean,表示布尔值,true 和 false
- null、undefined
- symbol,表示唯一、不可变的值
- 复合类型
- array,数组
- tuple,元组
- enum,枚举
- 对象类型
- object,表示任意对象(除了 number、string、boolean、symbol、null、undefined 之外的类型)
- interface,接口,用于描述对象的结构,可重复使用
- 函数类型
- function,表示函数类型
- void,表示函数没有返回值
- any,表示任意类型
- 高级类型
- union types,联合类型,允许一个变量可以是多种类型中的一种
- intersection types,交叉类型,用于将多个类型合并为一个类型
interface 给 Function/Array/Class(indexable)做声明
- 函数
1
2
3
4
5
6
7// 描述了一个函数类型,该函数接收两个参数并返回一个数字
interface MyFunction {
(arg1: string, arg2: number): void;
}
let myAdd: MyFunction = function (x, y) {
return x + y
} - 数组
1
2
3
4
5// 描述了一个具有数字索引签名的字符串数组。意味着我们可以通过数字索引来访问数组元素。
interface MyArray {
[index: number]: string;
}
let myArray: StringArray = ['Bob', 'Fred']; - 类
1
2
3
4
5
6
7interface StringDictionary {
[index: string]: string;
}
let myDict: StringDictionary = {
name: 'Bob',
age: '30'
};