typescript

一、typescript 中interfacetype差异

1.1 类型区别

1、type(类型别名) 用来给一个类型起新名字,使用 type 创建类型别名。类型别名不仅可以用来表示基本类型,还可以用来表示对象类型、联合类型、元组和交集。

type userName = string; // 基本类型
type userMsg = string | number; // 联合类型

// 对象类型```ts
type Person = {
  name: userName;
  age: number;
};

// 使用Person类型
let user: Person = {
  name: "leo",
  age: 18,
};

2、interface(接口) 接口是命名数据结构(例如对象)的另一种方式;与 type 不同,interface 仅限于描述对象类型与函数类型。

接口的声明语法也不同于类型别名的声明语法。如将上面的类型别名 Person 重写为接口声明如下所示。

interface Person {
  name: userName;
  age: number;
}

// 定义一个函数类型的接口
interface MathFunction {
  (x: number, y: number): number;
}

1.2 相同之处

1、都可以用来定义对象和函数 type

type Position = {
  x: number;
  y: number;
};

type SetPosition = (x: number, y: number) => void;
interface;

interface Position {
  x: number;
  y: number;
}

interface SetPosition {
  (x: number, y: number): void;
}

2、都可以实现继承 type 和 interface 并不互斥。type 可以继承 interface,反之亦然。只是在实现形式上,稍微有些区别。

type


// type 继承 type
type Person {
    name: string
}

type Student = Person & { stuId: number }

// type 继承 interface
interface Person {
    name: string
}

type Student = Person & { stuId: number }
interface

// interface 继承 interface
interface Person {
    name: string
}

interface Student extends Person { stuId: number }

// interface 继承 type
type Person {
    name: string
}

interface Student extends Person { stuId: number }

小结:对于 interface 来说,继承是通过 extends 实现的;而 type 是通过 & 来实现的,也可以叫做交叉类型。

1.3 不同之处

1、type 可以做到而 interface 不能做到 type 可以声明基本类型。

type userName = string; type 可以声明联合类型。

type userMsg = string | number; type 可以声明元组类型。

type Data = [number, string]; type 可以通过 typeof 操作符来声明

type myType = typeof someObj;

2、interface 可以做到而 type 不能做到 interface 可以声明合并。

interface test {
  name: string;
}

interface test {
  age: number;
}

/*
    test实际为 {
        name: string
        age: number
    }
*/

如果是 type 的话,就会报重复定义的警告,因此是无法实现声明合并的。

1.4 使用建议

1、官方推荐使用 interface,其他无法满足需求的情况下用 type。但是因为联合类型和交叉类型是比较常用的,所以避免不了大量使用 type 的场景,一些复杂类型也需要通过组装后形成类型别名来使用。

2、如果想保持代码统一,还是可选择使用 type。通过上面的对比,type 其实可涵盖 interface 的大部分场景。

3、对于 React 组件中 props 及 state,推荐使用 type,这样能够保证使用组件的地方不能随意在上面添加属性。如果有自定义需求,可通过 HOC(高阶组件)二次封装。

4、编写三方库时使推荐使用 interface,其更加灵活自动的类型合并可应对未知的复杂使用场景。

二、typescript 的 any、unknown、以及 never 区别及使用场景

TypeScript 中的 anyunknownnever 都是用于处理不确定类型的概念,但它们在使用和意图方面有一些重要的区别。

  1. any:

    • any 表示任何类型,它关闭了 TypeScript 的类型检查,允许你在变量上执行任何操作,类似于普通 JavaScript。
    • any 通常用于逐渐迁移现有 JavaScript 代码到 TypeScript,或者处理具有动态类型的情况,但应谨慎使用,因为它减弱了类型检查。
    let myVar: any = 5;
    myVar = "Hello"; // 合法,但不推荐
    
  2. unknown:

    • unknown 也表示任何类型,但它强制进行类型检查,不允许你执行任意操作,直到你明确地将其转换为其他类型。
    • unknown 通常用于处理不清楚类型的情况,要求你在使用前明确地进行类型检查或转换。
    let myVar: unknown = 5;
    // myVar.toFixed(2); // 错误,需要进行类型检查或转换
    if (typeof myVar === "number") {
      const result = myVar.toFixed(2); // 合法,已进行类型检查
    }
    
  3. never:

    • never 表示永远不会有值的类型。它通常用于表示抛出异常、无限循环或不会返回结果的函数。
    function throwError(message: string): never {
      throw new Error(message);
    }
    
    function infiniteLoop(): never {
      while (true) {
        // 无限循环
      }
    }
    

使用场景示例:

  • 使用 any 应该非常谨慎,最好只在需要与动态类型集成的特殊情况下使用,或者逐渐迁移现有 JavaScript 代码到 TypeScript 时使用。
  • 使用 unknown 当你无法确定变量的类型,但需要在使用前进行类型检查或转换。
  • 使用 never 当你有一些代码永远不应该返回结果或总是抛出异常的情况。这可以帮助你编写更严谨的代码。

总的来说,尽量避免使用 any,而是使用更严格的类型检查。使用 unknown 来处理不确定类型的情况,并使用类型断言或类型守卫进行类型转换。使用 never 来表示不可到达的代码或抛出异常的情况。这些类型可以帮助你更安全和可维护的编写 TypeScript 代码。

Contributors: masecho