类型检查机制

类型推断

ts会根据表达式 右边的值 来推断 左边变量的类型

当有多个不同类型的值时,ts会推断出一个兼容性强的通用类型

类型断言

当你完全确定值的类型时,ts提供一种方法,允许你推倒它的类型推断,就是类型断言

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

类型断言可以增加代码的灵活性,在改造一些旧代码时非常方便,但是使用类型断言要避免滥用,要对上下文环境有充足的预判,没有任何根据的类型断言会给代码带来安全隐患

类型兼容

当一个类型Y可以被赋值给另一个类型X时,我们就可以说类型X兼容类型Y

X兼容Y : X(目标类型) = Y (源类型)

接口兼容

1
2
3
4
5
6
7
8
9
10
11
12
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 }
y = x //y接口要求要有三个属性,而x有两个,满足条件

函数兼容

函数兼容要满足三个条件: 满足参数个数,满足参数类型,满足返回值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
type Handler = (a: number, b: number) => void
function hof(handler: Handler) {
return handler
}

//1. 参数个数
let handle1 = (a: number) => { }
hof(handle1)
let handle2 = (a: number,b: number,c: number) => { }
hof(handle2) // Handler需要两个参数,而这里给了三个,则不满足条件
// 可选参数
let a = {p1: number, p2: number} => {}
let b = {p1?: number, p2?: number} =>{}
let c = (...args: number[]) => {}
a = b
a = c // 固定参数可以兼容可选参数和剩余参数
b = c b = a //错误 // 可选参数不兼容固定参数和剩余参数


//2. 参数类型
let handle3 = (a: string) => { }
hof(handle3)//参数类型不同,不能兼容

interface Ponit3D {
x: number,
y: number,
z: number
}
interface Point2D {
x: number,
y: number
}
let p3d = (point: Ponit3D) => { }
let p2d = (point: Point2D) => { }
p3d = p2d // 对象的兼容规则是 对象成员多的兼容成员个数少的 与接口兼容的规则相反
p2d = p3d // 错误

//3. 返回值类型
let f = () => ({ name: 'Alice' })
let g = () => ({ name: 'Alice', location: 'Beijing' })
f = g
g = f // 错误

枚举兼容

1
2
3
4
5
enum Fruit { Apple, Banana }
enum Color { Red, Blue }
let fruit: Fruit.Apple = 3
let no: number = Fruit.Apple
let color: Color.Red = Fruit.Apple // 错误 // 枚举之前是不兼容的

类兼容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
constructor(p: number, q: number) { }
id: number = 1
private name: string = 'zhangsan'
}
class B {
static s = 1
constructor(p: number) { }
id: number = 2
private name: string = 'zhangsan' //当类中有私有成员,不构成兼容,只有子类能够兼容
}
let aa = new A(1, 2)
let bb = new B(1)
aa = bb // 静态成员和构造函数不参与比较,如果他们有相同的实例成员,他们的实例就可以兼容
bb = aa // 这里两个实例都具有相同的实例成员id

泛型兼容

1
2
3
4
5
6
7
interface Empty<T>{
value: T
}
let obj1: Empty<number> = {} //当接口中没有成员的时候可以兼容
let obj2: Empty<string> = {} //当接口中的类型参数 T 被接口成员使用了的时候,才会影响泛型的兼容性
obj1 = obj2

泛型函数

1
2
3
4
5
6
7
8
9
let log1 = <T>(x: T): T => {
console.log('x');
return x
}
let log2 = <U>(y: U): U => {
console.log('y');
return y
}
log1 = log2 // 当两个泛型函数的定义相同,但是没有指定类型参数,他们之间是可以兼容的

结构之间兼容:成员少的兼容成员多的

函数之间兼容:参数多的兼容参数少的

类型保护

ts能够在特定的区块中保证变量属于某种确定的类型

可以在此区块中放心的引用此类型的属性,或调用此类型的方法

1
2
3
4
5
6
7
8
9
10
11
//1. 使用 instanceof
//2. 使用 in
//3. 使用 typeof
let a = String
if (a instanceof String) {
a.charAt(0)
}
else if (a instanceof Number) {
a.toFixed(2)
}