TypeScript基础之联合类型、交叉类型、类型别名

本文深入探讨了TypeScript中的联合类型和交叉类型。联合类型表示一个值可以是多种类型之一,如`number|string|boolean`。交叉类型则将多个类型合并为一个,例如`A&B`。在接口扩展和类型约束方面,同名属性的类型必须兼容。此外,还介绍了类型别名的使用,以及type与interface的区别,包括声明合并和映射类型等功能。
摘要由CSDN通过智能技术生成

联合类型

联合类型表示取值可以为多种类型中的一种,使用 | 分割每个类型。

let a: number | string | boolean;
a = 100;
a = 'xman';
a = true;

函数中使用:

const test = (info: string | number) => {
  // ...
}

约束取值

const num: 1 | 2 = 1;
type isMan = true | false;
type EventNames = 'click' | 'scroll' | 'mousemove';

以上1 | 2, true | false, ‘click’ | ‘scroll’ | 'mousemove’被称为字面量类型,分别为数字、布尔、字符串字面量类型,可以用来约束取值只能是其中几个值中的一个。

交叉类型

交叉类型是将多个类型合并为一个类型,使用&定义交叉类型。

可以将多个接口类型合并成一个类型,实现等同于接口继承的效果

interface A {
  name: string;
  age: number;
}

interface B {
  name: string;
  height: string;
}

type Person =  A & B;  // 相当于求并集
let person: Person = { name: 'xman', age: 18, height: '60kg'};

注意:

  1. 原子类型可以合并吗?
    如果仅仅把基本类型、字面量类型、函数类型等原子类型合并成交叉类型,是没有任何用处的。因为任何类型都不能满足同时属于多种原子类型。
type Useless = string & number;  // 就是个never
  1. 合并的接口类型存在同名属性是什么效果?
    如果同名属性的类型兼容,比如一个是number,另一个是number的子类型、数字字面量类型,合并后属性的类型就是两者中的子类型。
interface A {
  name: string;
  age: number;
  id: number;
}

interface B {
  name: string;
  height: string;
  id: string;
}

type Person =  A & B;  
let person: Person = { name: 'xman', age: 18, height: '60kg', id: 1};
// Type 'number' is not assignable to type 'number & string'.  
// Type 'number' is not assignable to type 'string'.

以上代码提示name属性值出现类型错误。

interface A {
  name: string;
  age: number;
  id: number;
}


interface B {
  name: string;
  height: string;
  id: 1;  // 数字字面量类型为1
}

type Person =  A & B;  
let person: Person = { name: 'xman', age: 18, height: '60kg', id: 1};

以上代码中interface B的name属性的类型就是数字字面量类型1,因此我们不能把任何非1之外的值赋值为name属性;
下面看下同名属性非基本数据类型的合并情况:

interface A {
  info: {
    name: string
  }
}

interface B {
  info: {
    age: number
  }
}

interface C {
  info: {
    bool: boolean
  }
}

type ABC = A & B & C;
let abc: ABC = {
  info: {
    name: 'xman',
    age: 18,
    bool: true
  }
}

编译成功,结果如下:

var abc = {
    info: {
        name: 'xman',
        age: 18,
        bool: true
    }
};

类型别名

type: 其作用就是给类型起一个新名字,可以作用于原始值(基本类型)、联合类型、元祖以及其他任何你需要手写的类型。
例子:

// 基本类型
type Num = Number;
let n: Num = 1;

// 联合类型
type Shape = { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number };

function area(s: Shape) {
  if (s.kind === "circle") {
    return Math.PI * s.radius * s.radius;
  } else if (s.kind === "square") {
    return s.x * s.x;
  } else {
    return (s.x * s.y) / 2;
  }
}
  
// 对象
type UserInfo = {name: string};

// 函数
type GetInfo = () => string;

// 元祖 
type Size = [number, number];
let x: Size = [10, 99.9]; 

注意:
起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。给基本类型起别名通常没什么用。

type与interface区别

1. 都可以描述一个对象或者函数

interface

interface IPerson {
  name: string;
  age: number;
}

interface SetPerson {
  (name: string, age: number):void;
}

type

type Person = {
  name: string;
  age: number;
}

type SetPerson = (name: string, age: number) => void;

注: 在ts编译成js后,所有的接口和type 都会被擦除掉

2. 拓展

接口可以扩展,但type不能extends和implement,但是type可以通过交叉类型实现interface的extends行为。interface 可以extends type,同时type也可以与interface类型交叉

  1. interface extends interface, type 交叉类型可以实现
interface IAnimal {
  name: string;
}

interface IPerson extends IAnimal{
  age: number;
}


// type 交叉类型可以实现
type Animal = {
  name: string;
}

type Person = Animal & { age: number};
  1. interface extends type, type 与 interface 交叉
type Animal = {
  name: string;
}

interface IPerson extends Animal {
  age: number;
}

// type 与 interface 交叉
interface IAnimal {
  name: string
}
type Person = IAnimal & {
  age: number;
}

3. Implements

类可以以相同的方式实现接口或类型别名。但是请注意,类和接口被认为是静态的。因此,它们不能实现/扩展命名联合类型的类型别名

interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x: 1;
  y: 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x: 1;
  y: 2;
}

type PartialPoint = { x: number; } | { y: number; };

// A class may only implement another class or interface.
class SomePartialPoint implements PartialPoint {
  x: 1;
  y: 2;
}

4. extends class

类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。

class Point {
  x: number;
  y: number;
}
interface Point3d extends Point {
  z: number;
}

5. type可以声明基本类型别名、联合类型、元祖等类型

type Num = Number;  

// 联合类型
type Shape = { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number };

// 元祖 
type Size = [number, number]; 

type还可以使用typeof获取实例的类型进行赋值

let div = document.createElement('div'); 
type B = typeof div;

6. 声明合并

接口可以定义多次,并将被视为单个接口(即所有声明属性的合并),而type不能定义多次

interface IPerson {
  name: string;
  age: number;
}
interface IPerson {
  height: string;
}
let person:IPerson = {
  name: 'xman',
  age: 18,
  height: '60kg'
}

7. 映射类型

type 能使用in关键字生成映射类型,但interface不行
语法与索引签名的语法类型,内部使用了 for … in。 具有三个部分:

  • 类型变量 K,它会依次绑定到每个属性。
  • 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
  • 属性的结果类型。
type Keys = 'name' | 'height';

type Person = {
  [key in Keys]: string;     // 类型for... in
}

let person: Person = {
  name: 'xman',
  height: '60kg'
}

其他

export default interface IPerson {
  name: string;
  age: number;
}

type Person = {
  name: string;
}
export default Person;

参考资料

https://www.tslang.cn/docs/handbook/declaration-merging.html
Interfaces vs Types in TypeScript - Stack Overflow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值