干货!TS常见错误分析!

3,944 阅读2分钟

前言:在初学者使用Ts进行项目开发,或者将js项目改造为Ts项目的过程中,常遇到诸多问题。今天就来总结一下常见的Ts错误类型吧!

1.TS2322

interface CSSProperties {

  display: 'block' | 'flex' | 'grid';

}

const style = {

  display: 'flex',

};

// TS2322: Type '{ display: string; }' is not assignable to type 'CSSProperties'.

//  Types of property 'display' are incompatible.

//   Type 'string' is not assignable to type '"block" | "flex" | "grid"'.

const cssStyle: CSSProperties = style;

在上面的例子中,CSSProperties 的 display 属性的类型是字符串字面量类型 'block' | 'flex' | 'grid',虽然变量 style 的 display 属性看起来与 CSSProperties 类型完全兼容,但是 TypeScript 提示了 TS2322 类型不兼容的错误。这是因为变量 style 的类型被自动推断成了 { display: string },string 类型自然无法兼容字符串字面量类型 'block' | 'flex' | 'grid',所以变量 style 不能赋值给 cssStyle。

解决方法:

// 方法 1
const style: CSSProperties = {
  display: 'flex',
};

// 方法 2
const style = {
  display: 'flex' as 'flex',
};
// typeof style = { display: 'flex' }

2. TS2345

enum A {
  x = 'x',
}

enum B {
  x = 'x',
}

function fn(val: A) {}

fn(B.x); // TS2345: Argument of type 'B.x' is not assignable to parameter of type 'A'.

如上面的例子所示,此时 TypeScript 提示了类型不匹配的错误。这是因为枚举是在运行时真正存在的对象,因此 TypeScript 并不会判断两个枚举是否可以互相兼容。

可以使用类型断言绕过 TypeScript 的类型检查:

function fn(val: A) {}

fn((B.x as unknown) as A);

3.TS2589

type RepeatX<N extends number, T extends any[] = []> = T['length'] extends N
  ? T
  : RepeatX<N, [...T, 'X']>;

type T1 = RepeatX<5>; // => ["X", "X", "X", "X", "X"]
// TS2589: Type instantiation is excessively deep and possibly infinite.
type T2 = RepeatX<50>; // => any

它是由泛型实例化递归嵌套过深造成的。

因为泛型 RepeatX 接收了一个数字类型入参 N,并返回了一个长度为 N、元素都是 'X' 的数组类型,所以第1个类型 T1 包含了 5 个 "X" 的数组类型。但是第2个类型 T2 的类型却是 any,并且提示了 TS2589 类型错误。这是因为 TypeScript 在处理递归类型的时候,最多实例化 50 层,如果超出了递归层数的限制,TypeScript 便不会继续实例化,并且类型会变为 top 类型 any。

对于上面的错误,我们使用 @ts-ignore 注释忽略即可。

4.TS2554

function toString(x: number | undefined): string {
  if (x === undefined) {
    return '';
  }
  return x.toString();
}

toString(); // TS2554: Expected 1 arguments, but got 0.
toString(undefined);
toString(1);

它是由于形参和实参个数不匹配造成的。

5.TS1169

interface Obj {
  [key in 'id' | 'name']: any; // TS1169: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.
};

它是在接口类型定义中由于使用了非字面量或者非唯一 symbol 类型作为属性名造成的

6.TS2456

// TS2456: Type alias 'T' circularly references itself.
type T = Readonly<T>;

它由于类型别名循环引用了自身造成的

参考:

  1. 来自 100 +项目经历的常见 TypeScript 错误汇总分析
  2. TypeScript 源码仓库