【TypeScript】交叉类型&联合类型(四)

【TypeScript】交叉类型&联合类型(四)

一、简介

TypeScript 中的交叉类型联合类型是用来组合多个类型的方式。

  • 交叉类型
    交叉类型(Intersection Types)使用&符号将多个类型组合在一起,表示同时具备这些类型的特性。
  • 联合类型
    联合类型(Union Types)使用|符号将多个类型组合在一起,表示可以是其中任意一个类型。

二、交叉类型

交叉类型 , 简单来说就是通过&符号将多个类型进行合并成一个类型,然后用type来声明新生成的类型。

这里我举个例子,具体如下:

ts 复制代码
type A = { foo: number };
type B = { bar: string };
type C = A & B;

const obj: C = { foo: 123, bar: "abc" };

在上面的例子中,类型 C 是类型 A 和类型 B 的交叉类型,表示同时具备 foo 和 bar 属性。变量 obj 符合交叉类型 C 的定义,拥有 foo 和 bar 属性。这就是一个典型的交叉类型。

2.1 交叉类型使用的注意点

在使用交叉类型时,有几个注意点需要考虑:

问:任何类型都能通过 & 合并成新的类型吗?

:这肯定是 不行 的,原子类型进行合并是没有任何意义,因为它们合并后的类型是 never,比如 string & number,这肯定是错误的,因为不可能有既满足字符串又能满足数字类型的值.

ts 复制代码
type A = string & number; // 错误:基本类型无法进行交叉操作

问:交叉的类型中具有同名属性,该怎么处理?

:这里分两种情况,如果同名属性的类型相同则合并后还是原本类型,如果类型不同,则合并后类型为never

  • 合并后是string
ts 复制代码
type A = { foo: string };
type B = { foo: string };
type C = A & B; // 合并后是never

const obj: C = { foo: "abc" }; // 使用类型断言解决冲突
  • 合并后是never
ts 复制代码
type A = { foo: number };
type B = { foo: string };
type C = A & B; // 合并后是never

const obj: C = { foo: "abc" }; // 报错, 可以使用这个避免错误 { foo: "abc" as never }; 
2.2 基本数据类型交叉
  • any和number交叉结果是any类型
  • any和boolean交叉结果是any类型
  • any和string交叉结果是any类型
  • any和never交叉结果是never类型。

注意:any 类型和除 never 类型以外的任何类型交叉时都为any

ts 复制代码
type A = any & 1; //any
type B = any & boolean; //any
type C = any & never; //never

let Aname: A = 'lining'
let Bname: B = 'lining'

其他情况比较:

ts 复制代码
type A = number & 1; //1
type B = 'maoxiansheng' & string; //'maoxiansheng'
type C = boolean & true; //true
2.3 对象类型交叉
  • 键的类型是对象类型
    A、B、C三个类型都有相同的键inner,但是键的数据类型不同,分别是D、E、F,此时A&B&C会将inner键的类型进行合并,其实是D、E、F的交叉类型。
ts 复制代码
interface A {
    inner: D;
}
interface B {
    inner: E;
}
interface C {
    inner: F;
}

interface D {
    d: boolean;
}
interface E {
    e: string;
}
interface F {
    f: number;
}

交叉类型使用

ts 复制代码
type ABC = A & B & C;
let abc: ABC = {
    inner: {
        d: false,
        e: 'className',
        f: 5
    }
};
  • 键的类型是字面量类型或字面量联合类型
    字面量类型是可辨识的类型,当键的类型是不同的字面量类型,则交叉后类型为never类型。

    ts 复制代码
    type A = {
       kind:'a',
       loyal:number
    }
    type B = {
       kind:'b',
       loyal:string
    }
    
    type AB = A&B;//never
  • 函数类型的交叉运算
    函数类型的交叉运算会使用ts中函数重载来实现。

    ts 复制代码
    type A = (a:number,b:number) => void
    type B = (a:string,b:string) => void
    type AB = A&B;
    
    let func:AB = (a:number | string ,b:number | string) => {} 
    func(1,2)//正常
    func('a','b')//正常
    func(1,'b')//报错


    由于联合后,没有对应的func(number, string)类型的参数,因此会报出错误,解决上面的问题,只需要再加一个数据类型,其中 a为number类型,b为string类型。具体如下:

    ts 复制代码
    type A = (a:number,b:number) => void
    type B = (a:string,b:string) => void
    type C = (a:number,b:string) => void
    type ABC = A&B&C;
    
    let func:ABC = (a:number | string ,b:number | string) => {} 
    func(1,2)//正常
    func('a','b')//正常
    func(1,'b')//正常

相信小伙伴能够看懂这里的逻辑了吧,就是交叉的类型需要成对的匹配,那假如再出现需要传递的参数是func(string,number)类型的参数,有应该如何处理?只需要再添加新的类型即可:type C = (a:string,b:number) => void

但是,实际操作可能不需要这么麻烦,除非必要必须这样做。通常我们会有更加简单的方案直接定义。

三、联合类型

联合类型和交叉类型比较相似,联合类型通过 | 符号连接多个类型从而生成新的类型。

它主要是取多个类型的交集,即多个类型共有的类型才是联合类型最终的类型 。联合类型可以是多个类型其中一个,可做选择,比如:string | number,它的取值可以是string类型也可以是number类型。

举几个例子,如下所示:

  • 声明变量的时候设置变量类型

    ts 复制代码
    let a:string|number|boolean;
    a = 's';
    a = 1;
    a= false;
  • 多个接口类型进行联合

    ts 复制代码
    interface X{
    	q:number,
    	w:string,
    	r:string
    }
    interface Y{
    	q:number
    	r:string,
    }
    type XY = X | Y
    let value:XY = {
        q:1,
        r:'r'
    }
    
    let value2:XY = {
        q:1,
        r:'r',
        w: 'w'
    }

    错误演示,多余 x 属性。

    ts 复制代码
    interface X{
    		q:number,
    		w:string,
    		r:string
    	}
    interface Y{
    	q:number
    	r:string,
    }
    type XY = X | Y
    let value3:XY = {
        q:1,
        r:'r',
        x: 'x' // Error,Type '{ q: number; r: string; x: string; }' is not assignable to type 'XY'.
    }
  • 函数接口类型进行联合

    ts 复制代码
    interface X{
    	x:()=> string;
    	y:()=> number;
    }
    interface Y{
    	x:()=>string;
    }
    type XY = X|Y;
    function func1():XY{
    //此处不进行类型断言为XY在编辑器中会报类型错误
      return {} as XY
    
    }
    
    let testFunc = func1();
    testFunc.x();
    testFunc.y(); //Error:类型"XY"上不存在属性"y",类型"Y"上不存在属性"y"。

另外我们还要注意,**testFunc.x()**还会报类型错误,我们需要用类型守卫来区分不同类型。这里我们用 in 操作符来判断

ts 复制代码
if('x' in testFunc) testFunc.x()

扩展:boolean 类型可以看成是 true | false 的联合类型

四、类型缩减

  • 当字面量类型和原始类型进行联合,那么就会造成类型缩减。

    ts 复制代码
    type A = 'a' | string;  //string类型
    type B = false | boolean; //bolean 类型
    type C = 1 | number; //number类型

如上,A是由字面量 a 和原始类型string组成,则会缩减为string类型。

  • 枚举也会有类型缩减现象,如下:

    ts 复制代码
    enum Class{
       A,
       B
     }
    type C = Class.A | Class;   //Class类型

注意⚠️:TS会把字面量类型和枚举成员类型给缩减掉,只剩下原始类型和枚举类型

当接口类型进行联合,接口中同名属性的类型不同,该怎么进行缩减呢?比如下面的例子

ts 复制代码
interface A{
   name:string
}
interface B{
   name:string | number
   [property:string]:any
}
type AB = A|B

会缩减为B类型,可以实际查看该运行结果

ts 复制代码
interface A{
   name:string
}
interface B{
   name:string | number
   [property:string]:any
}
type AB = A|B


let nameA: AB = { name: '' }
let nameB: AB = { name: 123 }
let nameC: AB = { name: 123, count: 256 }

以上就是TypeScript中交叉类型和联合类型的说明。感觉对自己有用的客观请不要吝啬你手中的三连,谢谢。


相关推荐
大树885 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠5 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush45 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5206 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz6 小时前
Maven依赖冲突
java·服务器·maven
不会C语言的男孩7 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈7 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix
程序猿阿伟8 小时前
《Chrome离线扩展安装的底层逻辑与场景落地指南》
服务器·网络·chrome
凡人叶枫8 小时前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
AC赳赳老秦9 小时前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw