兼容的意义
实际工作时,往往无法做到类型一致
兼容的多种情况
- 简单类型的兼容---小赋值给大
type A = string | number // 小赋值给大 const a: A = 'hi' - 普通对象的兼容---属性少的兼容属性多的,即属性多的赋值给属性少的
// 例子 1 type Person = { name: string, age: number } type User = { name: string, age: number, id: string, email: string } const user: User = { name: 'rourou', age: 18, id: '001', email: '001@001.com' } // 属性多的赋值给属性少的 const person: Person = user // 例子 2 const f1 = (p: Person): void => { console.log(p) } // User类型的对象作为参数 赋值给Person类型的对象 f1(user) - 接口的兼容---属性少的兼容属性多的,即属性多的赋值给属性少的
// 例子 1 存在继承关系 interface Person { name: string, age: number } interface User extends Person { id: string, email: string } const user: User = { name: 'rourou', age: 18, id: '001', email: '001@001.com' } const p: Person = user // 例子 2 不存在继承关系 interface 有左手的人 { left: string } interface 有双手的人 { left: string, right: string } const a_p: 有双手的人 = { left: 'l', right: 'r' } const l_p: 有左手的人 = a_p - 函数的兼容
- 参数个数不同时,多参数的函数兼容少参数的函数(即少参数的函数可以赋值给多参数的函数)(因为多数情况下,js程序员不会把所有的参数都传过去即只传需要的参数)
注: 这种情况很常见,例如给元素注册事件// 例子 1 参数个数不同的函数的兼容 type oneParam = (a: number) => void type twoParams = (a: number, b: string) => void let f1 = (a: number) => { console.log(a) } let f2 = (a:number, b: string) => { console.log(a, b) } let fOneParam: oneParam = f1 fOneParam = f2 // 报错 let fTwoParams: twoParams = f1 fTwoParams = f2const button = document.getElementById('submit') const fn = (e: MouseEvent) => { console.log(e) } button.addEventListener('click', fn) // 第三个参数不传默认为 false button.addEventListener('click', fn, false) // 冒泡 button.addEventListener('click', fn, true) // 捕获- 参数个数相同,但类型不同时,参数类型个数多的兼容类型个数少的(即参数类型个数少的可以赋值给参数类型个数多的)
tip: 可以这样记忆,参数相当于凸出来的齿子的木块(齿子可以收回),所以参数多的可以赋值的参数少的(因为多的可以收回);而参数个数相同类型不同的函数 相当于凹进去齿子的木块,比如凹进去两个齿子和凹进去三个齿子,凸出来两个、三个的都可以赋值给凹进去两个的,但凸出来两个的没办法赋值给凹进来三个的。interface MouseEvent { target: string } interface MyMouseEvent extends MouseEvent { x: number, y: number } let f1 = (e: MouseEvent) => { console.log(e) } let f2 = (e: MyMouseEvent) => { console.log(e) } f1 = f2 // 报错 f2 = f1
在工作中的应用
interface Event {
target: string
}
interface MyMouseEvent extends Event {
x: number,
y: number
}
function listenEvent(eventType: string, fn: (e: Event) => void) {
// 一些操作
}
// 错误的函数调用
// 类型 “(e: MyMouseEvent) => void”的参数不能赋给类型“(e: Event) => void”的参数。
// 参数“e”和“e” 的类型不兼容。
// 类型“Event”缺少类型“MyMouseEvent”中的以下属性: x, y
// 即:参数个数相同,类型不同时,参数类型个数少的可以赋值给参数类型个数多的
// 可以在tsconfig.json文件中修改条件。这样就可以多的赋值给少的了
"compilerOptions": {
"strictFunctionTypes": false
}
listenEvent('click', (e: MyMouseEvent) => {
console.log(e.x, e.y)
})
// 正确的函数调用
listenEvent('click', (e: Event) => {
// 断言后可以拿到 x y 的值
console.log((e as MyMouseEvent).x, (e as MyMouseEvent).y)
})
- 函数返回值属性不同(同对象赋值)
let 返回值属性少集合大 = () => {
return { name: 'rourou' }
}
let 返回值属性多集合小 = () => {
return { name: 'rourou', age: 18 }
}
返回值属性多集合小 = 返回值属性少集合大
返回值属性少集合大 = 返回值属性多集合小
1. unknown 是所有类型的并集,并集不能直接用,需要类型收窄后才可以用。即 unknown 只能赋值给 any 和 unknown。
2. 类型小范围可以赋值给大范围
3. object 是个并集,不包含基本类型(非严格模式下:除了 null、undefined)
4. never 是最小的集合,可以赋值给任何类型
5. void、undefined、null 之间的赋值(根据 tsconfig.json 来配置)
6. undefined 可以赋值给 void,即没有返回值的函数可以 return undefined
6. unknown 是顶类型可以接受任何类型的赋值,never 是底类型不接受任何类型的赋值