Skip to content

类型推断

类型检查机制包含: 类型推断 类型兼容性 类型保护

不需要指定变量类型或函数的返回值类型,ts 可自动推断出类型

类型推断包括: 基础类型推断 最佳通用类型推断 上下文类型推断

typescript
// 基础类型推断
// 最佳通用类型推断
let a = 1
let b = [1]
let b1 = [1, null]

let c = (x = 1) => {}
let c1 = (x = 1) => x + 1

// 上下文推断
// window.onkeydown = (event) => {  // KeyboardEvent
// 	console.log(event.button);
// };

类型断言

typescript
interface Foo {
  bar: number
}
let foo = {} as Foo
foo.bar = 1

注意: 类型断言可以将某一变量特指为某种类型,但并不会产生接口约束作用,如下举例

typescript
interface Foo {
  bar: number
}
let foo = {} as Foo
// foo.bar = 1	不进行赋值

foo 被指定为 Foo 接口类型,但真实的 foo 是一个空对象,没有 foo.bar =1相当于没有 bar 属性,所以此时的接口并不能起到约束作用。 解决: 直接在声明的时将对象指定为接口类型,而不使用类型断言

typescript
{
  interface Foo {
    bar: number
  }

  let foo: Foo = {}
}

{
  interface Foo {
    bar: number
  }
  // let foo = {} as Foo;
  // foo.bar = 1;
  let foo: Foo = {
    bar: 1,
  }
}

image.png

各种类型之间的兼容性

tsconfig.json => "strictNullChecks": false,(个人不推荐)

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

let s: string = 'a'
s = null

接口兼容性

接口类型相当于对变量属性的约束,必须全部满足才可以兼容赋值

typescript
// 接口兼容性
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: a, b: 2, c: 3 }
// 将变量y赋值给变量x, 测试接口X是否兼容接口Y
x = y // 源类型Y具备目标类型X的全部必要属性

// 将变量x赋值给变量y, 测试接口Y是否兼容接口X
y = x // 源类型X不具备目标类型Y的全部必要属性(不含c属性)

image.png

函数的兼容性

目标函数能够兼容源函数,需同时满足以下三个条件: 参数的个数(目标函数的参数个数一定要多于源函数的参数个数) 参数的类型(参数类型必须匹配) 返回值的类型(标函数的返回值类型 与 原函数的返回值类型 必须相同或为其子类型)

typescript
// 函数兼容性
type Handler = (a: number, b: number) => void

function hof(hadler: Handler) {
  return hadler
}

函数兼容性的影响因素

参数个数

typescript
let hadler1 = (a: number) => {} // 少参数
hof(hadler1)

let hadler2 = (a: number, b: number, c: number) => {} // 多参数
hof(hadler2)

// 当参数不固定时,固定参数与可选参数,剩余参数的兼容性
{
  // 可选参数和剩余参数
  let a = (p1: number, p2: number) => {}
  let b = (p1?: number, p2?: number) => {}
  let c = (...args: number[]) => {}

  // tsconfig.json -> "strictFunctionTypes": false,
  a = b // 固定参数可以兼容可选参数
  a = c // 固定参数可以兼容剩余参数

  // tsconfig.json ("strictFunctionTypes": false可兼容)
  // b = c; // 可选参数不兼容固定参数
  // b = a; // 可选参数不兼容剩余参数
  c = a // 剩余参数不兼容固定参数
  c = b // 可选参数不兼容剩余参数
}

image.png

参数类型

基础类型
typescript
// 函数兼容性
type Handler = (a: number, b: number) => void
function hof(hadler: Handler) {
  return hadler
}

let hadler3 = (a: number) => {}
hof(hadler3)

// let hadler3 = (a: string) => { }
// hof(hadler3)

image.png

对象类型
typescript
interface Point3D {
  x: number
  y: number
  z: number
}
interface Point2D {
  x: number
  y: number
}

let p2d = (ponit: Point2D) => {}
let p3d = (ponit: Point3D) => {}

// tsconfig.json ("strictFunctionTypes": false可兼容)
p2d = p3d // Point2D 不兼容 Point3D
p3d = p2d // Point3D 兼容 Point2D

image.png

返回值类型

typescript
let f1 = () => ({ name: 'Alice' })
let g = () => ({ name: 'Alice', location: 'Beijing' })

// 成员少的兼容成员多的
f1 = g
g = f1

image.png

小技巧: > 返回值类型,和接口类似相当于是对函数的一种约束,所以必须在满足要求的前提先才可以兼容 > 即参数可以多了我不用,但绝对不可以少

函数重载对函数兼容性的影响

typescript
// 函数定义-重载列表
function overload(a: number, b: number): number
function overload(a: number, b: number): string
// 函数实现
// 兼容: 目标函数参数个数 > 源函数参数个数
function overload(a: any, b: any): any {}

// 不兼容: 目标函数参数个数 < 源函数参数个数
// function overload(a: any, b: any, c: any): any {}

// 不兼容: 删除返回值
// function overload(a: any, b: any) {}

枚举类型的兼容性

typescript
// 枚举兼容性
enum Fruit {
  Apple,
  Banana,
}

enum Color {
  Red,
  Yellow,
}

let fruit: Fruit.Apple = 1
let no: number = Fruit.Apple
// 不同枚举之间不兼容
// let color: Color.Red = Fruit.Apple;

image.png

类的兼容性

typescript
// 类的兼容性
// 静态成员和构造函数不参与类的兼容性比较
class A {
  constructor(p: number, q: number) {} // 构造函数
  id: number = 1
  private name: string = '' // 私有成员
}

class B {
  static s: number = 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)
aa = cc // 父类兼容子类
cc = aa // 子类兼容父类

泛型的兼容性

typescript
// 当类型参数T未被成员使用是,不会影响泛型兼容性
interface Empty<T> {}
let obj1: Empty<number> = {}
let obj2: Empty<string> = {}
obj1 = obj2

// 当类型参数T被成员使用时,才会影响泛型兼容性
interface Empty1<T> {
  attr: T
}
let obj3: Empty1<number> = { attr: 1 }
let obj4: Empty1<string> = { attr: '1' }
// obj3 = obj4;

// 泛型函数的兼容性
// 定义相同的两个泛型函数,如果未指定具体类型,则可相互兼容
let t1 = <T>(x: T): T => {
  console.log('x')
  return x
}

let t2 = <U>(y: U): U => {
  console.log('y')
  return y
}

t1 = t2 // 未指定类型的两个定义相同的泛型函数,可相互兼容

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

类型保护机制

typescript
enum Type {
  Strong,
  Week,
}

class Java {
  helloJava() {
    console.log('Hello Java')
  }
  java: any // 新增java属性
}

class JavaScript {
  helloJavaScript() {
    console.log('Hello JavaScript')
  }
  javascript: any // 新增javascript属性
}

function getLanguage(type: Type) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  return lang
}

getLanguage(Type.Strong)

TS不能准确判断实例到底是哪一种类型,因为TS不能准确判断实例到底是哪一种类型

image.png

使用类型断言

typescript
function getLanguage(type: Type) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  if ((lang as Java).helloJava) {
    ;(lang as Java).helloJava()
  } else {
    ;(lang as JavaScript).helloJavaScript()
  }
  return lang
}

创建类型保护区块

使用instance关键字创建区块

typescript
function getLanguage(type: Type, x: string | number) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  if (lang instanceof Java) {
    lang.helloJava()
  } else {
    lang.helloJavaScript()
  }
  return lang
}

通过in关键字判断变量所属类型来创建区块

typescript
function getLanguage(type: Type, x: string | number) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  if ('java' in lang) {
    lang.helloJava()
  } else {
    lang.helloJavaScript()
  }
  return lang
}

通过typeof对参数x的类型进行判断,创建对应的区块

typescript
function getLanguage(type: Type, x: string | number) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  if (typeof x === 'string') {
    x.length // string
  } else {
    x.toFixed(2) // number
  }
  return lang
}

使用类型保护函数

typescript
function getLanguage(type: Type, x: string | number) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  if (isJava(lang)) {
    lang.helloJava
  } else {
    lang.helloJavaScript
  }
  return lang
}

学习笔记出自于梁宵老师课程