# 类型推断
在没有明确指出类型的地方,类型推断会帮助提供类型
# 示例 1
let x = 3;
x = "3"; //Error:不能将类型“string”分配给类型“number”。
1
2
2
这里 x 被推断为 number,所以再次赋值为 string 是错误的
类型推断是从有向左推断的
# 示例 2
定义初始类型以及部分成员初始值
interface Foo {
bar: number;
}
let foo = <Foo>{};
foo.bar = 1;
1
2
3
4
5
2
3
4
5
# 示例 3
定义初始值
interface Foo {
bar: number;
}
let foo: Foo = {
bar: 1,
};
1
2
3
4
5
6
2
3
4
5
6
# 示例 4:类型断言
interface Foo {
bar: number;
}
let foo = {} as Foo; //类型断言
foo.bar = 1;
1
2
3
4
5
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
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
这是可以的,因为 (x: any) => any matches (y: any) => any