1. 对 TypeScript 的理解
typescript 是静态类型语言,它向 js 添加了类型语法,可以提前检测出错误,提高代码的质量和健壮性。
TS 和 JS 的区别在于静态类型语言(变量声明时类型就是确定的,之后不允许再修改)和动态类型语言(运行阶段才会确定变量类型,变量类型随时可改变)。
补充:和 java/c++ 这种静态强类型语言不同,ts 能够兼容 js 语法,所以是静态、弱类型语言。TS 会被编译成 JS 再运行。
- TS 跨平台:可应用在任何操作系统上,linux、windows、mac...
- ES6 特性:TS 支持 ES6 的大部分特性
- TS 是面向对象的语言
2. mudule 和 namespace 的区别
module:包含顶级import
或export
的文件都被当作一个模块,如果文件中两者都不包含,那么它的内容视为全局可见的。
namespace:namespace
是全局namespace下的一个 JS 对象,主要是为了避免命名冲突,不同命名空间中的标识符不会发生冲突。
区别在于 module 可以声明它的依赖、可以更好地复用代码,而 namespace 很难识别组件之间的依赖关系,后期的维护成本高。 因此大型应用不推荐使用 namespace。
补充:.ts
的文件最后会编译成.js
文件,这时候类型信息就丢失了,.d.ts
文件可以在写js
代码时提供一些类型信息。
3. 对泛型的理解
泛型<T>
允许将类型作为参数进行传递以创建可重用的组件,让该组件不仅能支持当前的数据类型,也能支持未来的数据类型。
// “类型变量 T” 捕获用户传入的类型,之后就可以使用这个类型
function identity<T>(args: T): T {
return args;
}
// 泛型类
class GenericNumber<T> {
value: T;
add: (x: T, y: T) => T;
}
let geNumber = new GenericNumber<number>();
// 泛型约束,限制函数去处理带有 .length 属性的所有类型
interface Lengthwise {
length: number;
}
function identity<T extends Lengthwise>(args: T): T {
console.log(args.length);
return args;
}
4. 枚举、联合类型、交叉类型、元组、字面量类型
使用枚举类型enum
可以定义一些带名字的常量,并将取值限定在一定范围内。成员默认从 0 开始递增。
和普通枚举相比,常量枚举不支持动态计算,并且常量枚举在编译阶段会被删除、避免性能消耗。
enum state {
error = "ERROR",
success = "SUCCESS"
}
// 常量枚举,会严格地限制访问
const enum Directions {
Up = 1,
Down
}
联合类型|
的取值可以是多种类型中的一种。当访问联合类型的值时,只能访问所有类型共有的成员。
let tmp = string | number;
交叉类型&
会将多个类型合并为一个类型,合并后的对象将拥有所有类型的成员。
type Response = Person & Loggable & ArtData;
Tuple元组类型[string, number]
表示一个已知元素数量和类型的数组,各元素的类型不需要相同。
let tmp: [string: number] = ['Tom', 25];
字面量类型有三种:字符串、数字和布尔值。
5. 类型别名 type 和 Interface 的区别
type
用于给一个类型起新的名字。
type Point = {
x: number;
y: number;
}
interface
用于描述一个对象的结构和类型,在前后端开发时起到一个约定的作用。也可以给函数、数组、类做声明。
interface Person {
readonly id: number; // 只读属性
name: string; // 必须属性
age?: number; // 可选属性
}
两者区别:
- type 可以声明基本类型、联合类型、元组等;
- Interface 的声明重名时可以合并,type 重名会报错。
6. any 和 unknown
两者都用于描述不确定的类型,更推荐使用unknown
。
unknown
的变量在使用前需要先进行类型判断(typeof
)来缩小类型范围,因此比较安全。- 任何类型都可以分配给
any
和unknown
。any
可以分配给任何类型,但unknown
只能分配给unknown
或any
。 any
和任何类型T
的交叉类型为any
,unknown
和任何类型T
的交叉类型为T
。- (忽略)
any
和任何类型T
的联合类型为any
,unknown
和任何类型T
的联合类型为unknown
。
function fn(x: unknown) {
let v1: any = x;
let v2: unknown = x;
let v3: string = x; // Error
}
7. never 和 void
- 含义:
void
表示没有任何类型,never
表示永不存在的值的类型。 - 函数返回值:当一个函数返回空值时,返回值为
void
类型;当一个函数根本没有返回值时(或抛出异常),返回值为never
类型。 - never 是所有类型的子类型。
8. const 和 readonly
- const 用于变量,readonly 用于属性。
- 当用于对象时,readonly 表示对象属性不能改变,而 const 表示对象引用不改变、属性可变。
9. infer 关键字
infer
表示类型推导,只能用在 extends 语句、声明的变量只能用在 true 分支。它会在类型没有推导时进行占位,等到推导成功后就返回正确的类型。
T extends (...args: any) => infer P
- 忽略
infer P
而从整体来看,这段代码实际表示 T 是不是一个函数类型; (...args: any) => infer P
这段代码实际表示一个函数类型,其中使用P
将返回类型进行占位;- 如果 T 是函数类型,那么返回函数的返回类型
P
;如果不是函数类型就返回never
。
10. 类和访问修饰符
extends
会从父类中继承属性和方法。
访问修饰符决定了类成员的可访问性,共有三种:
- 默认
public
:类中的所有成员、其子类和类的实例都可以被访问; private
:不能在类的外部访问;protected
:和 private 区别在于protected
的成员可以被子类中访问(但在该类的实例中无法访问)。
readonly
是只读修饰符,不是访问修饰符。
11. extends 和 implements 的区别
- 使用 implements 的类需要实现相关类的所有属性和方法;
- 使用 extends 的子类会继承父类的所有属性和方法。
12. TS 中的方法重载
方法重载是指在一个类中定义多个同名的方法,但每个方法具有不同数量的参数或者参数类型不同。
- 方法重载能够对功能相似的方法进行统一,更容易记住。
- 重载方法应该完成类似的功能。
- 重载方法的返回值类型应该相同。
13. 类型断言Assertion
当你知道某个值更具体的类型时,可以通过类型断言as
告诉编译器,手动指定更具体的类型。
let v1: any = "this is a string";
// 推荐写法:值 as 类型
let vLength: number = (v1 as string).length;
// 其他写法:<类型>值
let vLength: number = (<string>v1).length;
14. 实用工具类型
Partial<T>
:将类型 T 的所有属性都设置为可选的,返回类型是输入类型的子集。Required<T>
:和 Partial 相反,所有属性都是必选的。Readonly<T>
:将类型 T 的所有属性都设置为可读的、不能修改。Pick<T, K>
:从 T 中挑选出属性 K。Omit<T, K>
:从 T 中移除属性 K。Exclude<T, ExcludedUnion>
:移除 T 中的 U 集合属性。Extract<T, Union>
:Exclude 的反操作,取 T、U 的交集属性。Record<Keys, Type>
:构造一个类型,key 的类型为 K,value 的类型为 T。Parameters<T>
:获取一个函数的所有参数类型。ReturnType<T>
:由函数类型 T 的返回值类型构建出的新类型。ConstructorParameters<T>
:获取一个类的构造函数参数类型。InstanceType<T>
:由构造函数类型 T 的实例类型构建出的新类型。NonNullable<T>
:从类型 T 中移除 null、undefined 属性。
15. TS 中的类型
- number、string、boolean、void、null、undefined
- enum、interface、tuple、array、class...
16. declare 关键字
因为 JS库或框架 中是没有 TS 声明文件的,我们在 TS 中使用它们时需要加declare
以避免编译错误。这时 TS 运行时会把声明的库变量赋值为any
类型,跳过编译检查。
import 'vue-router'
declare module 'vue-router' {
//...
}
17. 使用TS判断某函数的传入参数是否为数组类型
function isArray(x: unknown): boolean {
if (Array.isArray(x)) return true;
return false;
}
18. ?. / ?? / ! 等符号的含义
?.
可选链运算符可以读取对象的深层属性,而且不用验证引用是否有效。当遇到null、undefined
时会结束运行,此时返回值是undefined
。??
空值合并运算符。当左侧操作数为null、undefined
时返回右侧操作数,否则返回左侧操作数。!
,!x
将从 x 值域中排除null、undefined
。
19. 装饰器 Decorator
装饰器是一种特殊类型的声明,可以被附加到类声明、方法/属性、访问符或参数上。
使用@expression
的形式,在不改变原类、继承的情况下动态地扩展对象功能。
20. tsconfig.json 有什么作用
可以设置不同的选项来告诉编译器如何编译当前项目。
- include:需要包含的文件;
- exclude:要排除的文件。
参考: