Typescript - 基础(五)

TS 类型检查机制:TS 编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为。作用是辅助开发,提高开发效率。

1、类型推断

不需要指定变量的类型(函数的返回值类型),TS 可以根据某些规则自动地为其推断出一些类型。

1.1 基础类型推断

let a ;        // 如果不进行赋值,则 TS 推断类型为 ang
let b = 1;    // 此时,TS 推断 b 的类型为 string
let c = [];    // c 的类型被推断为 any[],即以 any 为类型的数组
let d = (x = 1) => x + 1;       // 为 x 添加默认值 1,则会推断入参类型为 number,根据计算结果,推断返回值为 number。

1.2 最佳通用类型推断

当需要从多个类型中推断出一个类型时, ts 就会尽可能推断出一个兼容当前所有类型的通用类型。

let b = [1, null];     //b 的类型为:(number | null)[];

1.3 上下文类型推断

1.1 、1.2 讲的都是根据结果推断数据类型,即从右向左推断。那么从左向右推断就属于上下文类型推断。通常会发生在事件处理中。

window.onkeydown = (event) => {
  console.log(event.code);
  console.log(event.button) ;      // Error,event 无 button 属性,编译时会报错
}  // 此处 event 会被推断为 KeyboardEvent 类型,并且能够提示我们 event 具有哪些属性

1.4 类型断言

当 TS 的类型推断不符合预期,此时 TS 提供了一个方法,允许覆盖 TS 的推断,就是类型断言。

let foo = {};
foo.bar = 1;      //此处会报错,应为 bar 未被定义
// 定义一个接口
interface Foo {
  bar: number;
}
let foo = {} as Foo;      // 类型断言的使用,不建议这样使用
let foo1: Foo = {    // 建议使用
  bar : 1
}

2、类型兼容性

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

let s : string = 'a';
// 如果此时将tsconfig.json 中的 strictNullChecks 设置为 false,  那么此时 s = null 是不会报错的。

我们可以任务 null 类型是 string 的子类型,也就是说 string 兼容 null 类型。

类型兼容性的例子,广泛存在于接口、函数、类中。

  • 接口兼容
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

Y 接口具备 X 接口的所有属性,虽然 Y 有额外的属性,但仍旧可认为是 X 的类型。源类型具有目标类型的必要属性,就可以赋值。简而言之,成员少的会兼容成员多的。

  • 函数的兼容
type Handler = (a: number, b: number) => void
function hof(handler: Handler) {
    return handler
}

函数兼容需要满足的几个条件如下:

  • 参数个数的要求
  • 参数类型
  • 返回值类型
// 1)参数个数
let handler1 = (a: number) => {}
hof(handler1)
let handler2 = (a: number, b: number, c: number) => {}
hof(handler2)      // Error

// 可选参数和剩余参数
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

// 2)参数类型
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,参数成员个数多的要兼容参数成员个数少的

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

2.2 函数重载

function overload(a: number, b: number): number      // 目标函数
function overload(a: string, b: string): string                // 目标函数
function overload(a: any, b: any): any {}                      // 源函数
// 目标函数的参数个数要多于原函数,否则函数重载报错

2.3 枚举兼容

enum Fruit { Apple, Banana }
enum Color { Red, Yellow }
let fruit: Fruit.Apple = 1        
let no: number = Fruit.Apple      // 枚举和 number 之间是可以兼容的
let color: Color.Red = Fruit.Apple    // Error  枚举之间是不兼容的

2.4 类兼容性

静态成员和构造函数是不参与比较的。如果两个类具有相同实例成员,那么他们的实例可以相互兼容。

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 = ''
}
class C extends A {}
let aa = new A(1, 2)
let bb = new B(1)      // aa 和 bb 是完全兼容的,如果 class A 和 class B 具有同名私有成员,那么 aa 和 bb 不可完全兼容

let cc = new C(1, 2)
aa = cc
cc = aa      // 可以看出父类和子类是完全兼容的

2.5 泛型兼容性

2.5.1 泛型接口
interface Empty<T> {}
let obj1: Empty<number> = {};
let obj2: Empty<string> = {};
obj1 = obj2        // 泛型接口中无任何成员,所以 obj1 和 obj2 可以完全兼容

interface Empty<T> {
  value: T
}          // 若接口定义中包含成员,那么 obj1 和 obj2 不兼容
2.5.2 泛型函数
let log1 = <T>(x: T): T => {
    console.log('x')
    return x
}
let log2 = <U>(y: U): U => {
    console.log('y')
    return y
}
log1 = log2      // 如果两个泛型的定义相同,但是没有指定类型参数,那么他们之间是可以相互兼容的

3、类型保护

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。