目录
基本
逆变与协变
准备工作
概念
逆变
协变
证明兼容性
逆变
协变
逆变带来的问题
禁止逆变
对于函数的参数来说:少的参数可以赋值给多的,因为人家内部实现
传递了多个,但是我用少的,安全
如果我多写了,就不安全了
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 = (arg:T) => void
type isArg = Arg extends Arg ? 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 = (arg: any) => T
type isReturn = Return extends Return ? true : false // true
class Parent {
house() {}
}
class Child extends Parent {
car() {}
}
class GrandSon extends Child {
sleep() {
console.log('sleep')
}
}
interface Array2 {
concat: (...args: T[]) => T[]
[index: number]: any
}
let arrParent!: Array2
let arrChild!: Array2
// **************** 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 {
/*
禁用逆变的效果,不去检测逆变问题
源码里面,都是采用下面的写法,不采用: 的写法
*/
concat(...args: T[]): T[]
// 默认开启逆变的效果
// concat2: (...args: T[]) => T[]
[index: number]: any
}
let arrParent!: Array2
let arrChild!: Array2
// **************** arrChild 能否赋值给 arrParent ? ****************
arrParent = arrChild // 报错,不符合逆变
/**
* 赋值的流程: 挨个比属性
* concat: (...args:Parent[]) => Parent[] = (...args:Child[]) => Child[]
*/
给接口声明函数
的时候,函数不要使用:的写法,源码里面都是采用下面的写法,不采用: 的写法