我正在参加「掘金·启航计划」
前言
除了上文说到的 联合类型
TS 类型系统还有哪些运算? 交叉类型 itersection types (交集)
交叉类型
type A = string & number
// A 的结果是 never
一般来说在做交叉运算的时候,不会用到简单类型上,只会用到对象上面。
type 有左手的人 = {
left: string
}
type 有右手的人 = {
right: string
}
// 有左手的人 并 有右手的人
type C = 有左手的人 | 有右手的人
// 有左手的人 交 有右手的人
type D = 有左手的人 & 有右手的人
const d: D = {
// 只给 left 不给 right 它就会报错 right is missing
left: 'yes'
}
type 有左手的人 = {
left: string
}
const a: 有左手的人 = {
left: '一米八',
right: '一米五' // 报错
}
根据交集图所示,有左手的人确实有可能有右手的。那么以上问题在哪?
TS 有时候松有时候紧
type 有左手的人 = {
left: string
}
const b = {
left: '一米八',
right: '一米五'
}
// 没有报错
const a: 有左手的人 = b
// 报错
const a2: 有左手的人 = {
left: '一米八',
right: '一米五'
}
当你在声明的时候,初始化的那一刻,你是不能有杂念的,也就是说 初始化的时候不能有任何东西
接口也能求交集
交集不一定只用在 type 上面
interfaceof Colorful {
color: string
}
interface Circle {
radius: number
}
type ColorfulCircle = Colorful & Circle
只要描述的是对象,不管是 type 描述,还是 interfaceof 描述,都可以。
模拟 User 继承 Person
type Person = {
name: string
age: number
}
type User = Person & {
id: number
email: string
}
const u: User = {
name: 'hone', age: 18, id: 1,
email: 'xxxx@gmail.com'
}
交叉类型的特殊情况
type Person = {
name: string
age: number
// 属性冲突,未报错
id: string
}
type User = Person & {
id: number
email: string
}
// 合并之后,这个 id 是 number 还是 string ?
// id 和 id 要求交集,最后 id 是 never
const u: User = { // 这个 User 类型不能用,但是并不报错
// id: 1, // never
name: 'hone', age: 18,
email: 'xxxx@gmail.com',
id: (1) as never // 可以自己骗自己写一个
}
当你在使用 type 做交集的时候,当 id 冲突了(只要有一个字段冲突了),整个类型虽然不报错,但并不能用,就好像 这个 User 类型实际上是 never。
type Person = {
name: string
id: "A"
}
type User = Person & {
id: "B"
email: string
}
const u: User = { // 这个 User 类型不能用,但是并不报错
// id: 1, // never
name: 'hone',
email: 'xxxx@gmail.com',
id: (1) as never // 可以自己骗自己写一个
}
// 如果把 id 改一哈
// 写一种骗自己的写法
type Person = {
name: string
id: "A"
}
type User = Person & {
id: "B"
email: string
}
const u: User = { // User 是 never
// 这个就不可以用了
// 因为 u 这个值是含有 never 的一个对象,而这个对象是不能赋值给 never 的
email: 'x' as never,
name: 'hone' as never
}
const a: User = { // User 是 never
email: 'x',
name: 'hone'
} as never // 除非在外面写成这样
type 和 interface 的第四个区别
interface Person {
id: string
name: string
}
interface User extends Person {
id: number
email: string
}
const a: User = {
email: 'x',
name: 'hone',
id: 1
}
用 type 不会报错,type 只会给你弄一个 never 出来,那个时候可能还没发现自己写错了,只有在用到 never值的时候才会发现,没有值可以用。
如果你希望你的东西,可扩展性强一点,尽量使用 interface, 那什么东西可拓展性强,就是对外提供的接口,容易被用户去扩展的,对内就没有必要去用 interface, 很少会自己扩展自己,因为可以自己改(内部可以随便改),扩展的多了就会导致层级过深很难用这个现象。
第四个区别:遇到属性冲突的时候,interface 会直接报错,而 type 只会弄出一个 never 。
其实 never 也可以看做一个 报错
type A = {
method: (n: number) => void
}
type B = {
method: (n: string) => void
} & A
// 请问 B 它的 method 还存不存在 ?
const b: B = {
method: (n) => {
n // number | string
}
}
console.log(b)
// 可以写 method ,那就是说 这两个 method 没有冲突 ?
以上写出来没有报错,那就说明有
type F1 = (n: number) => void
type F2 = (n: string) => void
type X = F1 & F2
// 函数的交集会得到一个参数并集
const x: X = (n) => { // number | string
console.log(n)
}
两个函数求交集会得到一个参数的并集
结论
交叉类型常用于有交集的类型 A、B
如果 A、B 无交集,可能得到 never,也可能只是属性是 never。
只要 never 出现了,这个类型它就不能用
理解 type A = {name: 'string'}
type A = {
name: string
}
type B = {
age: number
}
type C = A | B
const d = {
name: 'hone', gender: '男'
}
const c:C = d // 请问, d 赋值给 c 后 TS 会报错吗?
答案:不报错,d 可以赋值为 c, 因为这个 d 是 A 类型
type A = {name: string} 它并不单单指 name 为一个 key 的对象,它只是说 name 为 string,并没有做其他的限制。
type A = {
name: string
}
// 1. TS 在第一次声明的时候,它做的是严格检查
// 4. 第一次声明的时候这里别多加东西
const a: A = {
name: 'hone' , gender: '男'
}
// 2. 如果你这个对象是之前就声明好的,它就不做严格检查
type A = {
name: string
}
const a1 = {
name: 'hone' , gender: '男'
}
// 3. 因为你多一个属性对类型 A 来说无所谓
const a: A = a1