Libertus Chen-U
  1. 1 Libertus Chen-U
  2. 2 Last Surprise Lyn
  3. 3 Time Bomb Veela
  4. 4 Hypocrite Nush
  5. 5 One Last You Jen Bird
  6. 6 Life Will Change Lyn
  7. 7 かかってこいよ NakamuraEmi
  8. 8 BREAK IN TO BREAK OUT Lyn
  9. 9 Warcry mpi
  10. 10 Flower Of Life 发热巫女
2017-12-24 12:29:32

浅谈TypeScript中的类型兼容

和Java等基于名义类型的语言不同,TypeScript是基于结构子类型的语言。其数据类型的兼容性并不是通过明确声明类型名称来决定的,而是只使用其成员描述类型。就像鸭子类型的例子一样,不要求生来就是鸭子,只要会鸭子叫就是鸭子。


基础类型兼容

基本规则

基本规则就一条:如果x要兼容y,那么y至少具有与x相同的属性。用官方文档的例子来解释如下:

interface Named {
    name: string;
}

let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: 'Alice', location: 'Seattle' };
x = y;

这里当将y赋值给x时,编译器将检查x的每个属性看是否能在y中也找到对应属性,如果能全部找到则赋值兼容成功。


函数兼容

基本规则

比较两个函数是否兼容的关键在于比较参数和返回值是否兼容。先看下面的例子:

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

y = x; // OK
x = y; // Error

let a = () => ({name: 'Alice'});
let b = () => ({name: 'Alice', location: 'Seattle'});

a = b; // OK
b = a; // Error because x() lacks a location property

首先比较参数列表,需要被兼容的源函数x中的每个参数都能在兼容目标函数y中被找到,参数名称可以不必相同,类型相同即可。

然后比较返回值列表,需要目标函数的返回值是源函数的子类型,如果目标函数返回值是void,那么源函数返回值可以是任意类型。

参数双向协变

当比较函数参数类型时,不必要完全相同,只要源函数参数可以赋值给目标函数对应参数或者目标函数参数可以赋值给源函数对应参数即可。实例如下

let funcX = (num: number&boolean) => 0;
let funcY = (num: number, str: string) => 0;

funcY = funcX;

let funcA = (num: number) => 0;
let funcB = (num: number&boolean, str: string) => 0;

funcY = funcX;

以上两种兼容都是被允许的。

可选参数和剩余参数

剩余参数可以当做无限个可选参数, 原函数的可选参数不参与兼容性的基本规则比较中。例子如下:

let funcX = (ant: number,address:string,target?:string) => 0;
let funcY = (num: number, str?: string) => 0;

funcY = funcX;

funcX虽然有一个额外参数,但由于是可选参数不参与基本规则比较,兼容被允许。


枚举兼容

枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。但是不同枚举类型之间是不兼容的。例子如下:

enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };

let status = Status.Ready;
status = Color.Green;  //error

类兼容

类的兼容性和对象及接口的兼容性相似,区别在于类分为实例部分和静态部分,比较兼容性时只有实例成员会被比较,静态成员和构造函数不在比较范围内。例子如下:

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

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

let a: Animal;
let s: Size;

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

私有成员

如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。


泛型兼容

泛型会在确定类型后再对其属性进行兼容性比较。例子如下:

interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // okay, y matches structure of x

上面虽然传入的泛型参数不同,但确定类型后其属性都为空,允许兼容。再看下面的例子:

interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // error, x and y are not compatible

这个例子中确定类型后,data属性类型不相同,则不允许兼容。

如果没有指定泛型类型,则所有的泛型参数都会被当成any比较。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「TypeScript」 标签。