Structural type system
1. Interface compatibility
- If the incoming variable does not match the declared type, TS will perform a compatibility check
- The principle is Duck-Check, which means that as long as the attribute variables declared in the target type exist in the source type, they are compatible.
ts
interface Animal {
name: string;
age: number;
}
interface Person {
name: string;
age: number;
gender: number;
}
// 要判断目标类型`Person`是否能够兼容输入的源类型`Animal`
function getName(animal: Animal): string {
return animal.name;
}
let p = {
name: "hello ",
age: 10,
gender: 0,
};
getName(p);
//只有在传参的时候两个变量之间才会进行兼容性的比较,赋值的时候并不会比较,会直接报错
let a: Animal = {
name: "hello ",
age: 10,
gender: 0,
};2. Basic type compatibility
ts
//基本数据类型也有兼容性判断
let num: string | number;
let str: string = "hello ";
num = str;
//只要有toString()方法就可以赋给字符串变量
let num2: {
toString(): string;
};
let str2: string = "jiagou";
num2 = str2;3. Class Compatibility
- In TS, it is a structural type system, which only compares structures and does not care about types.
ts
class Animal {
name: string;
}
class Bird extends Animal {
swing: number;
}
let a: Animal;
a = new Bird();
let b: Bird;
//并不是父类兼容子类,子类不兼容父类
b = new Animal();ts
class Animal {
name: string;
}
//如果父类和子类结构一样,也可以的
class Bird extends Animal {}
let a: Animal;
a = new Bird();
let b: Bird;
b = new Animal();ts
//甚至没有关系的两个类的实例也是可以的
class Animal {
name: string;
}
class Bird {
name: string;
}
let a: Animal;
a = new Bird();
let b: Bird;
b = new Animal();4. Function compatibility
- When comparing functions, you must first compare the parameters of the function, and then compare the return value of the function.
4.1 Comparison parameters
ts
type sumFunc = (a: number, b: number) => number;
let sum: sumFunc;
function f1(a: number, b: number): number {
return a + b;
}
sum = f1;
//可以省略一个参数
function f2(a: number): number {
return a;
}
sum = f2;
//可以省略二个参数
function f3(): number {
return 0;
}
sum = f3;
//多一个参数可不行
function f4(a: number, b: number, c: number) {
return a + b + c;
}
sum = f4;4.2 Compare return values
ts
type GetPerson = () => { name: string; age: number };
let getPerson: GetPerson;
//返回值一样可以
function g1() {
return { name: "hello ", age: 10 };
}
getPerson = g1;
//返回值多一个属性也可以
function g2() {
return { name: "hello ", age: 10, gender: "male" };
}
getPerson = g2;
//返回值少一个属性可不行
function g3() {
return { name: "hello " };
}
getPerson = g3;
//因为有可能要调用返回值上的方法
getPerson().age.toFixed();5. Covariance and contravariance of functions
Covariant: only in the same direction;
Contravariant: Only in the opposite direction;
Bidirectional covariance (Bivariant): including the same direction and different directions;
Invariant: If types are not identical, they are incompatible.
A ≼ B means A is a subtype of B.
A → B refers to the function type with A as the parameter type and B as the return value type.
x : A means x is of type A
The return value type is covariant, while the parameter type is contravariant
The return value type can be passed to the subclass, and the parameters can be passed to the parent class.
Parameters are inverse to the parent class and return values are covariant to the subclass. Help your father and return the shoes.
ts
class Animal {}
class Dog extends Animal {
public name: string = "Dog";
}
class BlackDog extends Dog {
public age: number = 10;
}
class WhiteDog extends Dog {
public home: string = "北京";
}
let animal: Animal;
let blackDog: BlackDog;
let whiteDog: WhiteDog;
type Callback = (dog: Dog) => Dog;
function exec(callback: Callback): void {
callback(whiteDog);
}
//不行 callback(redDog);
type ChildToChild = (blackDog: BlackDog) => BlackDog;
const childToChild: ChildToChild = (blackDog: BlackDog): BlackDog => blackDog;
exec(childToChild);
//也不行,理由同上
type ChildToParent = (blackDog: BlackDog) => Animal;
const childToParent: ChildToParent = (blackDog: BlackDog): Animal => animal;
exec(childToParent);
//不行 因为有可能调用返回的Dog的方法
type ParentToParent = (animal: Animal) => Animal;
const parentToParent: ParentToParent = (animal: Animal): Animal => animal;
exec(parentToParent);
//可以,所有的狗都是动物,返回的不管什么狗都是狗
type ParentToChild = (animal: Animal) => BlackDog;
const parentToChild: ParentToChild = (animal: Animal): BlackDog => blackDog;
exec(parentToChild);
//(Animal → Greyhound) ≼ (Dog → Dog)
//返回值类型很容易理解:黑狗是狗的子类。但参数类型则是相反的:动物是狗的父类!ts
// string | number|boolean 是 string | number的父类型
// string是string|number的子类型
type Callback2 = (a: string | number) => string | number;
function exec2(callback: Callback2): void {
callback("");
}
type ParentToChild2 = (a: string | number | boolean) => string;
const parentToChild2: ParentToChild2 = (a: string | number | boolean): string =>
"";
exec2(parentToChild2);
type Callback3 = (a: string | number) => string | number;
function exec3(callback: Callback2): void {
callback("");
}
type ParentToParent3 = (a: string) => string;
const parentToParent3: ParentToParent3 = (a: string): string => "";
exec3(parentToChild3);- In
TypeScript, parameter types are bidirectionally covariant, which means they are both covariant and contravariant, which is not safe.But now you can fix this problem inTypeScript 2.6version via--strictFunctionTypesor--strictflag
6. Generic compatibility
- When judging compatibility, generics will first judge the specific type, and then judge the compatibility.
ts
//接口内容为空没用到泛型的时候是可以的
//1.接口内容为空没用到泛型的时候是可以的
interface Empty<T> {}
let x!: Empty<string>;
let y!: Empty<number>;
x = y;
//2.接口内容不为空的时候不可以
interface NotEmpty<T> {
data: T;
}
let x1!: NotEmpty<string>;
let y1!: NotEmpty<number>;
x1 = y1;
//实现原理如下,称判断具体的类型再判断兼容性
interface NotEmptyString {
data: string;
}
interface NotEmptyNumber {
data: number;
}
let xx2!: NotEmptyString;
let yy2!: NotEmptyNumber;
xx2 = yy2;7. Enum compatibility
- Enumerated types are compatible with numeric types, and numeric types are compatible with enumerated types
- Different enum types are incompatible
ts
//数字可以赋给枚举
enum Colors {
Red,
Yellow,
}
let c: Colors;
c = Colors.Red;
c = 1;
c = "1";
//枚举值可以赋给数字
let n: number;
n = 1;
n = Colors.Red;