返回介绍

类型兼容性

发布于 2025-05-03 21:35:26 字数 3211 浏览 0 评论 0 收藏

类型兼容性用于确定一个类型是否能赋值给其他类型,这看起来并没有什么太大用处,而实际上当我们了解了兼容性之后才会规避之后实际编程中的很多低级错误。

结构类型

TypeScript 里的类型兼容性是基于「结构类型」的,结构类型是一种只使用其成员来描述类型的方式,其基本规则是,如果 x 要兼容 y ,那么 y 至少具有与 x 相同的属性。

我们做一个简单的实验,我们构建一个类 Person , 然后声明一个接口 DogDog 的属性 Person 都拥有,而且还多了其他属性,这种情况下 Dog 兼容了 Person

class Person {
    constructor(public weight: number, public name: string, public born: string) {

    }
}

interface Dog {
    name: string
    weight: number
}

let x: Dog

x = new Person(120, 'cxk', '1996-12-12') // OK

但如果反过来, Person 并没有兼容 Dog ,因为 Dog 的属性比 Person 要少了一个。

函数的类型兼容性

函数类型的兼容性判断,要查看 x 是否能赋值给 y ,首先看它们的参数列表。

x 的每个参数必须能在 y 里找到对应类型的参数,注意的是参数的名字相同与否无所谓,只看它们的类型。

这里, x 的每个参数在 y 中都能找到对应的参数,所以允许赋值:

let x = (a: number) => 0;
let y = (b: number, s: string) => 0;

y = x; // OK
x = y; // Error 不能将类型“(b: number, s: string) => number”分配给类型“(a: number) => number”。

那么当函数的参数中出现了可选参数或者 rest 参数时会怎么样呢?

let foo = (x: number, y: number) => {};
let bar = (x?: number, y?: number) => {};
let bas = (...args: number[]) => {};

foo = bar = bas;
bas = bar = foo;

如果你在 tsconfig.json 默认配置下上面的兼容性都是没问题的,但是在我们严格检测的环境下还是会报错的

原因就是可选类型的参数可能为 undefined ,在这种情况下不能与 number 兼容。

当我们把 strictNullChecks 设置为 false 时上述代码是兼容的。

那么甚至他们的参数数量都不一致呢?

let foo = (x: number, y: number) => {};
let bar = (x?: number) => {};

foo = bar // ok
bar = foo //报错

我们看到参数较多的 foo 兼容了 bar

枚举的类型兼容性

枚举与数字类型相互兼容:

enum Status {
  Ready,
  Waiting
}

let status = Status.Ready;
let num = 0;

status = num;
num = status;

类的类型兼容性

仅仅只有实例成员和方法会相比较,构造函数和静态成员不会被检查:

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) {}
}

class Size {
  feet: number;
  constructor(meters: number) {}
}

let a: Animal;
let s: Size;

a = s; // OK
s = a; // OK

私有的和受保护的成员必须来自于相同的类:

class Animal {
  protected feet: number;
}
class Cat extends Animal {}

let animal: Animal;
let cat: Cat;

animal = cat; // ok
cat = animal; // ok

class Size {
  protected feet: number;
}

let size: Size;

animal = size; // ERROR
size = animal; // ERROR

泛型的类型兼容性

泛型本身就是不确定的类型,它的表现根据是否被成员使用而不同。

就比如下面代码:

interface Person<T> {

}

let x : Person<string>
let y : Person<number>

x = y // ok
y = x // ok

由于没有被成员使用泛型,所以这里是没问题的。

那么我们再看下面:

interface Person<T> {
    name: T
}

let x : Person<string>
let y : Person<number>

x = y // 不能将类型“Person<number>”分配给类型“Person<string>”。
y = x // 不能将类型“Person<string>”分配给类型“Person<number>”。

这里由于泛型 T 被成员 name 使用了,所以类型不再兼容。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。