MST

星途 面试题库

面试题:TypeScript类型声明中的版本关系基础考察

请阐述TypeScript 2.x和3.x版本在类型声明方面的主要变化,并举例说明如何在实际代码中处理由于这些变化可能导致的兼容性问题。
30.9万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

主要变化

  1. 严格空值检查增强
    • 在TypeScript 2.x中,空值(null)和未定义(undefined)是可以赋值给任意类型的。例如:
// TypeScript 2.x
let num: number;
num = null; // 不会报错
- 到了3.x版本,开启`strictNullChecks`(默认开启)后,空值和未定义只能赋值给`void`、`null`和`undefined`类型。若要允许其他类型接受空值,需使用联合类型。例如:
// TypeScript 3.x
let num: number | null;
num = null; // 正确
let str: string | undefined;
str = undefined; // 正确
  1. 函数参数双向协变改为逆变
    • 在2.x中,函数参数类型是双向协变的。例如:
// TypeScript 2.x
interface Animal {}
interface Dog extends Animal {}
function handleAnimal(animal: Animal) {}
function handleDog(dog: Dog) {}
let animalFunc: (a: Animal) => void = handleDog; // 不会报错
- 3.x版本改为逆变,即参数类型更加严格。上述代码在3.x中会报错,因为函数参数类型赋值需要更严格匹配,`Dog`类型参数的函数不能赋值给`Animal`类型参数的函数。只有`Animal`类型参数的函数可以赋值给`Dog`类型参数的函数,因为`Dog`是`Animal`的子类型。例如:
// TypeScript 3.x
interface Animal {}
interface Dog extends Animal {}
function handleAnimal(animal: Animal) {}
function handleDog(dog: Dog) {}
let animalFunc: (a: Dog) => void = handleAnimal; // 正确
  1. 可选链操作符(Optional Chaining)
    • 3.x版本引入了可选链操作符?.。在2.x中,如果要访问对象深层属性,需要手动进行层层判断。例如:
// TypeScript 2.x
let user: { address: { city: string } } | undefined;
let city: string | undefined;
if (user && user.address) {
    city = user.address.city;
}
- 在3.x中,使用可选链操作符可以简化这种操作:
// TypeScript 3.x
let user: { address: { city: string } } | undefined;
let city = user?.address?.city;

兼容性问题处理

  1. 针对严格空值检查变化
    • 情况一:旧代码中允许空值赋值给非空类型
      • 检查所有变量声明,明确加上| null| undefined,如果不需要接受空值,确保空值不会被赋值。例如:
// 旧代码(TypeScript 2.x)
let name: string;
name = null;
// 新代码(TypeScript 3.x)
let name: string | null;
name = null;
// 或者,如果不需要接受空值
let name: string;
// 确保赋值逻辑中不会给name赋null
- **情况二:函数参数可能为null或undefined**
    - 在函数内部对参数进行空值检查。例如:
// 旧代码(TypeScript 2.x)
function printLength(str: string) {
    console.log(str.length);
}
printLength(null as any); // 运行时可能报错
// 新代码(TypeScript 3.x)
function printLength(str: string | null) {
    if (str) {
        console.log(str.length);
    }
}
printLength(null); // 不会报错
  1. 针对函数参数逆变变化
    • 情况一:之前双向协变允许的赋值现在报错
      • 检查函数赋值逻辑,确保函数参数类型匹配逆变规则。例如:
// 旧代码(TypeScript 2.x)
interface Shape {}
interface Rectangle extends Shape {}
function drawShape(shape: Shape) {}
function drawRectangle(rect: Rectangle) {}
let draw: (s: Shape) => void = drawRectangle; // 之前不报错,现在报错
// 新代码(TypeScript 3.x)
let draw: (s: Rectangle) => void = drawShape; // 正确
  1. 针对可选链操作符引入
    • 情况一:旧代码手动进行层层判断,可优化使用可选链操作符
      • 查找对象深层属性访问的代码块,用可选链操作符简化。例如:
// 旧代码(TypeScript 2.x)
let obj: { sub: { value: number } } | undefined;
let value: number | undefined;
if (obj && obj.sub) {
    value = obj.sub.value;
}
// 新代码(TypeScript 3.x)
let obj: { sub: { value: number } } | undefined;
let value = obj?.sub?.value;