# 类型推断

在没有明确指出类型的地方,类型推断会帮助提供类型

# 示例 1

let x = 3;
x = "3"; //Error:不能将类型“string”分配给类型“number”。
1
2

这里 x 被推断为 number,所以再次赋值为 string 是错误的

类型推断是从有向左推断的

# 示例 2

定义初始类型以及部分成员初始值

interface Foo {
  bar: number;
}
let foo = <Foo>{};
foo.bar = 1;
1
2
3
4
5

# 示例 3

定义初始值

interface Foo {
  bar: number;
}
let foo: Foo = {
  bar: 1,
};
1
2
3
4
5
6

# 示例 4:类型断言

interface Foo {
  bar: number;
}
let foo = {} as Foo; //类型断言
foo.bar = 1;
1
2
3
4
5

# 类型兼容

当一个类型 Y 可以被赋值给另一个类型 X 时,我们就可以说类型 X 兼容类型 Y ---------X 兼容 Y:X(目标类型)= Y(源类型)

# 简单示例

let a = {
  age: 1,
  name: "张三",
};
let b = {
  age: 1,
};
b = a;
a = b; //Error
1
2
3
4
5
6
7
8
9

# 接口兼容性

interface X {
  a: any;
  b: any;
}
interface Y {
  a: any;
  b: any;
  c: any;
}
let x: X = { a: 1, b: 2 };
let y: Y = { a: 1, b: 2, c: 3 };
x = y;
y = x; //Error
1
2
3
4
5
6
7
8
9
10
11
12
13

接口之间的兼容时候,成员少的兼容成员多的

# 函数兼容性

# 参数个数

type Handler = (a: number, b: number) => void;
function hof(handler: Handler) {
  return handler;
}
let handler1 = (a: number) => {};
hof(handler1);
let handler2 = (a: number, b: number, c: number) => {};
hof(handler2); //Error
1
2
3
4
5
6
7
8

函数兼容性参数多的可以兼容少的

# 可选参数和剩余参数

let a = (p1: number, p2: number) => {};
let b = (p1?: number, p2?: number) => {};
let c = (...args: number[]) => {};
a = b;
a = c;
b = a; //Error
b = c; //Error
c = a;
c = b;
1
2
3
4
5
6
7
8
9

固定参数兼容可选参数和剩余参数,可选参数不能兼容固定参数和剩余参数,剩余参数可以兼容固定参数和可选参数

# 参数类型

type Handler = (a: number, b: number) => void;
function hof(handler: Handler) {
  return handler;
}
let handler3 = (a: string) => {};
hof(handler3); //Error

interface Point3D {
  x: number;
  y: number;
  z: number;
}
interface Point2D {
  x: number;
  y: number;
}
let p3d = (point: Point3D) => {};
let p2d = (point: Point2D) => {};
p3d = p2d;
p2d = p23; //Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

函数兼容性遵循多的可以兼容少的,也就是函数参数只能少传,不能多传.与接口之间兼容性相反

# 函数返回值

let f = () => ({ name: "Alice" });
let g = () => ({ name: "Alice", location: "Beijing" });
f = g;
g = f; //Error
1
2
3
4

少的可以兼容多的

# 枚举兼容性

enum Fruit {
  Apple,
  Banana,
}
enum Color {
  Red,
  Yellow,
}
let fruit: Fruit.Apple;
let no: number = Fruit.Apple;
// let Color:Color.Red=Fruit.Apple //枚举之间不兼容
1
2
3
4
5
6
7
8
9
10
11

枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的

# 类兼容

class A {
  constructor(p: number, q: number) {}
  id: number = 1;
  // private name: string = "";
}
class B {
  static s = 1;
  constructor(p: number) {}
  id: number = 2;
  // private name: string = "";
}
let aa = new A(1, 2);
let bb = new B(1);
//有私有成员不兼容
aa = bb;
bb = aa;
class C1 extends A {}
let cc = new C1(1, 2);
cc = aa;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。 比较两个类类型的对象时,只有实例的成员会被比较。 静态成员和构造函数不在比较的范围内

类的私有成员和受保护成员会影响兼容性。 当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类

# 泛型兼容

interface Empty<T> {}
let obj1: Empty<number> = {};
let obj2: Empty<string> = {};
obj1 = obj2;
1
2
3
4

以上代码,obj1 和 obj2 是兼容的,因为他们的结构使用类型参数时并没有不同

interface Empty<T> {
  value: T;
}
let obj1: Empty<number>;
let obj2: Empty<string>;
obj1 = obj2; //Error
1
2
3
4
5
6

以上代码比前一个代码多了一个成员,所以对于没指定泛型类型的泛型参数时,会把所有泛型参数当成 any 比较,然后用结果类型进行比较,前一个例子

let log3 = <T>(x: T): T => {
  console.log(x);
  return x;
};
let log4 = <T>(y: T): T => {
  console.log(y);
  return y;
};
log3 = log4;
1
2
3
4
5
6
7
8
9

这是可以的,因为 (x: any) => any matches (y: any) => any

最后更新时间: 6/1/2021, 2:12:44 PM