interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
p = new Person(); // 不会报错
因为 Named 类型和 Person 类型的结构{ name: string }是一致的,但是在基于名义类型的语言中,如 C# 或 Java 中,这段代码会报错,因为 Person 类没有明确说明其实现了 Named 接口。
1.1 对象兼容
如果 x 要兼容 y,那么 y 至少具有与 x 相同的属性
interface Named {
name: string;
}
let x: Named;
let y = { name:'Alice', location: 'Seattle'};
x = y;
将 y 赋值给 x,目标为 x,此时就会检验,是否 x 中的每个属性,在 y 中都可以找到。只要都能找到,那么赋值就是允许的。
检验函数参数也是同样的
function greet(n: Named) {
console.log(n);
}
greet(y)
// 只要 Named 中的所有属性,y中都能找到,就可以。
// 至于多余的 location 属性,不会引发错误
这个属性查找的过程是递归的,检查每个成员及子成员。
1.2 函数兼容
1.2.1 参数兼容
let x = (a:number) => 0;
let y = (b:number,s:number) => 0;
x = y; // 错误,y函数的第二个参数必选,而x不接受
y = x; // 正确,x函数的每个参数,在y中都能找到对应类型的参数,且索引一致
let x = (a: number) => 0;
let y = (b: string, s: number) => 0;
y = x; // 错误,x的第一个参数为number,而y函数为string
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});
x = y; // OK
y = x; // Error, because x() lacks a location property
// 要求y中的返回值,x都要能返回. 这与参数兼容不一样
2. 枚举之间的兼容性
枚举类型与数字类型兼容,数字类型与枚举类型兼容. 但是枚举类型之间是不兼容的。
enum Status { Ready, Waiting }
enum Color { Red, Blue, Green };
let status1:number = Status.Ready; // 数字类型与枚举类型兼容
status1 = Color.Red; // 错误, 不同枚举类型不兼容
3. 类
类与对象字面量和接口差不多,除了:类有静态部分和实例部分的类型。
比较两个类类型的对象时,只有实例成员会被比较,静态成员和构造函数不在比较范围内。
class Animal {
feet: number;
constructor(name: string) {}
}
class Size {
feet: number;
constructor(feetNum: number) {}
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
// 因此对于Animal和Size来说,都有feet属性,而constructor方法虽然不一样,但是不会被比较
4. 泛型
类型参数只影响使用其作为类型一部分的结果类型:
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;
x = y; // OK, because y matches structure of x
因为虽然T最终的类型不一样,但是最终的interface并没有使用到T
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y; // Error, because x and y are not compatible
因为T最终的类型不一样,且最终的interface使用到了T,所以会不兼容