ts函数兼容性、逆变与协变

目录

基本

逆变与协变

准备工作

概念

逆变

协变

证明兼容性

逆变

协变

逆变带来的问题

禁止逆变


基本

对于函数的参数来说:少的参数可以赋值给多的,因为人家内部实现

  • 传递了多个,但是我用少的,安全

  • 如果我多写了,就不安全了

let sum3!:() => string | number
let sum4!:() => number

sum3 = sum4
let sum1 = (a: string, b: string) => a + b

let sum2 = (a: string) => a

sum1 = sum2					// 正常

sum2 = sum1
/*
	不能将  类型“(a: string, b: string) => string”  分配给  类型“(a: string) => string”。
	
	从安全性考虑: 定义的时候我可以定义多个,使用的时候我只使用1个,比如上面的,比如forEach
*/

逆变与协变

准备工作

tsconfig.js:

{
    /*
    	函数严格的类型检测
    		false:允许双向协变, 意味着 儿子爱传啥传啥【比如函数的参数】,丧失了逆变校验的能力
    		
    	下面的文档是 建立在 strictFunctionTypes: true的基础上的
    */
    "strictFunctionTypes": true
}
class Parent {
  house() {}
}
class Child extends Parent {
  car() {}
}
class GrandSon extends Child {
  sleep() {
    console.log('sleep')
  }
}

type Fn = (instance: Child) => Child
function fn(callback: Fn) {}

// 长的一样,不涉及兼容性
fn((instance: Child): Child => {
  return new Child()
})

概念

注意是通过 安全的角度 的来考虑的

  • 函数的参数逆变

  • 函数的返回值协变

逆变

逆变:使用函数的时候,传参可以传递之前参数的父类

fn((instance: Parent): Child => {
  return new Child()
})

传参可以传递之前参数的父类、之前参数

  • 之前参数的父类:只能使用 父类 的方法

  • 之前参数:能使用 父类、自己 的方法

协变

协变:使用函数的时候,返回值可以传递之前返回值的子类(传父则报错)

fn((instance: Child): GrandSon => {
  return new GrandSon()
})

证明兼容性

逆变

type Fn = (instance: Child) => Child
function fn(callback: Fn) {
    // callback参数可以传递: Child、GrandSon
    callback(new GrandSon())
}


// ************************* 假设 ************************************
type Fn = (instance: Child) => Child
function fn(callback: Fn) {
    callback(new GrandSon())
}
fn((instance:GrandSon): GrandSon => {
    return new GrandSon()
})
// 定义的是 Child, 传递的是 GrandSon,接收的时候最大是 Child, 接收的时候是:GrandSon 就会不安全,报错


type Fn = (instance: Child) => Child
function fn(callback: Fn) {
    callback(new Child())
}
fn((instance:GrandSon): GrandSon => {
    return new GrandSon()
})
// 定义的是 Child, 传递的是 Child,接收的时候最大是 Child, 接收的时候是:GrandSon 就会不安全,报错


// ************************* next ************************************
type Fn = (instance: Child) => Child
function fn(callback: Fn) {
    callback(new Parent())			// 报错,只允许传入 Child 以及 子类
}


// ************************* 逆变的证明 ************************************
type Arg<T> = (arg:T) => void
type isArg = Arg<Parent> extends Arg<Child> ? true: false		// true

协变

type Fn = (instance: Child) => Child
function fn(callback: Fn) {
  let r = callback(new GrandSon())
}

fn((instance: Child): GrandSon => {
  /**
   * r是 Child类型, 那么我返回 new GrandSon, GrandSon 是 Child 的子类型,安全
   */
  return new GrandSon()
})


// ************************* 假设 ************************************
type Fn = (instance: Child) => Child
function fn(callback: Fn) {
  let r = callback(new GrandSon())
}
// 报错,Parent 不是返回值 Child 的 子类型
fn((instance: Child): Parent => {
  return new Parent()
})


// ************************* 协变的证明 ************************************
type Return<T> = (arg: any) => T
type isReturn = Return<GrandSon> extends Return<Child> ? true : false	// true

逆变带来的问题

class Parent {
  house() {}
}
class Child extends Parent {
  car() {}
}
class GrandSon extends Child {
  sleep() {
    console.log('sleep')
  }
}

interface Array2<T> {
  concat: (...args: T[]) => T[]
  [index: number]: any
}

let arrParent!: Array2<Parent>
let arrChild!: Array2<Child>
// ****************  arrChild 能否赋值给 arrParent ?  ****************
    
arrParent = arrChild		// 报错,不符合逆变
/**
 * 赋值的流程: 挨个比属性
 * 	concat: 	(...args:Parent[]) => Parent[]   =  	(...args:Child[]) => Child[]
 */

禁止逆变

class Parent {
  house() {}
}
class Child extends Parent {
  car() {}
}
class GrandSon extends Child {
  sleep() {
    console.log('sleep')
  }
}

interface Array2<T> {
  /*
    禁用逆变的效果,不去检测逆变问题
    源码里面,都是采用下面的写法,不采用: 的写法
    */
  concat(...args: T[]): T[]

  // 默认开启逆变的效果
  // concat2: (...args: T[]) => T[]

  [index: number]: any
}

let arrParent!: Array2<Parent>
let arrChild!: Array2<Child>
// ****************  arrChild 能否赋值给 arrParent ?  ****************

arrParent = arrChild // 报错,不符合逆变
/**
 * 赋值的流程: 挨个比属性
 * 	concat: 	(...args:Parent[]) => Parent[]   =  	(...args:Child[]) => Child[]
 */

给接口声明函数的时候,函数不要使用的写法,源码里面都是采用下面的写法,不采用: 的写法

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值