TypeScript 联合类型(union type)

TS是JS的超集,在JS的基础上添加了一套类型系统,这样的TS可以被静态分析带来的好处显而易见。

let val: string = 'val';

声明一个string类型的变量val

let val: string = 'val';
val = 1; // Type 'number' is not assignable to type 'string'.

因为number类型和string类型并不兼容,在string类型值出现的地方并不能使用number类型指完成替换,所以在TS世界中给string类型的val变量赋值number类型的值会报错。

但是在JS中并没有赋值的限制:

// javascript
var val = 'val';
val = 1; // 这里不会报错

在JS中变量val首先被赋值了字符串,然后被赋值了数字,这两个数据类型并不一致,但是因为JS没有静态类型检查,所以这并不会报错。

TS的联合类型可以适应这种情况,表示这个变量可能是类型a也可能是类型b也可能是类型c等类型。

let val: string | number = 'val'
val = 1 // 这里并不会报错

在TS中,这里声明的变量val则表示可能是类型string也可能是类型number,所以对变量赋值string类型值和number类型值,并不会报错。

但问题也随之而来。

function test(val: string | number) {
  val.toLowerCase() // Property 'toLowerCase' does not exist on type 'string | number'.
}

TS会提示类型 string | number 上没有属性toLowserCase。

这个报错也很容易理解,因为类型string的数据上可以调用方法toLowerCase,但是number不可以。因为变量val的类型,string和number都有可能,TS并不能确定val在运行时是string类型,所以会出现这个错误。

我们需要一个方法来告诉TS这个变量现在是string类型。

使用typeof缩小类型范围

对于上面例子中的变量val.toLowerCase的报错来说是因为val的类型范围有点大(string | number),如果我们通过某种方式缩小该范围为string,那么在val上访问属性toLowserCase应当没有问题。

function test(val: string | number) {
  if (typeof val === 'string') {
    val.toLowerCase() // 这里并不会报错
    console.log('val是字符串')
  } else {
    console.log('val是数字')
  }
}

在TS中,if中的typeof val === 'string'这种形式的代码会被识别为类型保护(type guard)。TS会分析代码的执行流程,缩小变量可能的类型。在分支if (typeof val === ‘string’) 中变量val的类型被TS识别为’string’,所以在这个分支下val调用string类型数据的方法并不会报错。

因为val是string和number这两个类型的联合,TS不仅知道if子句中的val是string类型,还知道else中的val是number类型。

可以被typeof进行类型保护的类型有:

  • “string”
  • “number”
  • “bigint”
  • “boolean”
  • “symbol”
  • “undefined”
  • “object”
  • “function”

使用in缩小类型范围

上面介绍了对于基础类型的识别,在TS中使用频率更多的还有对象,使用typeof来区别不同对象显然是有问题的,因为typeof出来的结果都是’object’无法分辨两个不同的对象。

type A = {a: string}
type B = {b: string}

function test(val: A | B) {
  val.a // Property 'a' does not exist on type 'A | B'
}

在test函数中无论是访问val.a还是val.b都会报错,而原因已经明白,TS无法确定变量val的具体类型,TS并不知道当前是类型A还是类型B,所以我们需要帮他一把。

type A = {a: string}
type B = {b: string}

function test(val: A | B) {
  if ('a' in val) {
    console.log(val.a)
    return
  }
  console.log(val.b)
}

其中的in操作同样会被TS识别为类型保护,如果属性a存在于变量val中那么就能识别出val变量是类型A,则可以正常访问类型A中存在的属性a。

使用instanceof缩小类型范围

对于对象类型的区分除了使用操作符in还可以使用instanceof来完成。

function test(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  } else {
    console.log(x.toUpperCase());
  }
}

使用 === 和 == 缩小类型范围

使用严格等于(===)也可以在某些特别情况下正确缩小类型范围

function example(x: string | number, y: string | boolean) {
  if (x === y) {
    // We can now call any 'string' method on 'x' or 'y'.
    x.toUpperCase();

    y.toLowerCase();
  } else {
    console.log(x); // x 是 string | number

    console.log(y); // y 是 string | boolean
  }
}

在这个例子中xstringnumber的联合,而y是stringboolean的联合,当x === y的时候只可能xy都是string类型。所以在分支if (x === y) 中x和y的类型被正确识别为string类型。

我们知道在JS中宽松等于(== null)可以匹配null和undefined两种类型,当然TS也知道,所以 ==null,可以被用来识别类型。

interface Container {
  value: number | null | undefined;
}

function multiplyValue(container: Container, factor: number) {
  // Remove both 'null' and 'undefined' from the type.
  if (container.value != null) {
    console.log(container.value);
    // Now we can safely multiply 'container.value'.
    container.value *= factor;
  }
}

即使container.value可能是null或者undefined类型,但是在分支container.value != null中,该变量类型就只可能是number类型,所以其参与算术运算并不会报错。

区分联合类型

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;
  }
}

TS会通过参与联合的类型都有的属性kind来识别当前shape是Circle或者Square达到类型保护的目的。

类型谓词

上面介绍了TS对于联合类型中基础类型和对象类型的类型保护。但是这并不能覆盖全部的场景,例如上面介绍到的内容,并不能区分两个函数:

type fn1 = (arg: number) => boolean
type fn2 = (arg: string) => boolean

function test(fn: fn1 | fn2) {
  return fn(1)
}

在这个例子里,我们并没有方法来识别fn是类型fn1还是类型fn2。

在前面的例子里,例如 if (typeof a === ‘string’) 这里面的变量a会被TS类型系统识别为string,如果TS将识别一个变量为某个类型的能力开放给开发者,上面的问题就会迎刃而解。这个能力就是类型谓词。

通过观察上面类型保护的规律就会发现TS总会询问:变量a是类型A吗?被识别为类型保护的操作的回答总是true或者false都是boolean值。typoef a === ‘string’或者’a’ in A或者a instanceof A,这些操作的返回值都是boolean,并且都是和特定类型作比较。

// 我就是类型谓词形成的类型保护
function isTypefn1(fn: fn1 | fn2): fn is fn1 {
  if (fn.name === 'fn1') return true
  return false
}
function test(fn: fn1 | fn2) {
  if (isTypefn1(fn)) return fn(1)
  else return fn('1')
}

其中 isTypefn1调用的返回值就会告诉TS入参是不是类型fn1,这样TS就可以识别变量fn的类型完成类型保护。

参考

Narrowing

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TypeScript 支持以下数据类型:布尔型(Boolean)、数字(Number)、字符串(String)、数组(Array)、元组(Tuple)、枚举(Enum)、任意值(Any)、空值(Void)、Null 和 Undefined。 ### 回答2: TypeScript中有一些基本的类型,以及一些衍生的混合类型。以下是一些常见的类型: 1. 布尔类型(boolean):表示逻辑值,可以是true或false。 2. 数字类型(number):表示数值,可以包括整数、浮点数以及十六进制数等。 3. 字符串类型(string):表示文本值,可以包含任意字符序列。 4. 数组类型(array):表示一个特定类型的值的集合,可以包含多个元素。 5. 元组类型(tuple):表示一个固定长度的数组,数组中每个元素可以具有不同的类型。 6. 枚举类型(enum):表示一组具名的常量值。 7. 任意类型(any):表示任意类型的值,可以忽略类型检查。 8. 空类型(void):表示没有任何类型的值,可以用作函数的返回类型。 9. Null和Undefined类型:表示null或undefined值。 10. Never类型:表示永不存在的值的类型,通常用于表示函数的异常情况。 另外,TypeScript还支持更高级的类型,如联合类型union)、交叉类型(intersection)、类型别名(type alias)、类和接口等,这些类型可以帮助开发者更精确地描述变量和函数的类型。 ### 回答3: TypeScript具有以下类型: 1. 原始类型(Primitive types):包括布尔类型(boolean)、数字类型(number)、字符串类型(string)和空值/未定义类型(void、undefined、null)。 2. 数组类型(Array types):表示多个相同类型的值的集合,可以使用数组表示法或泛型表示法定义。 3. 元组类型(Tuple types):表示已知元素数量和类型的数组,每个元素可以是不同的类型。 4. 枚举类型(Enum types):用于定义具名常量的枚举类型,通过枚举成员来表示可能的值。 5. 函数类型(Function types):表示函数的类型,包括参数和返回值的类型。 6. 对象类型(Object types):表示非原始类型的值,如对象、数组、函数等。 7. 类类型(Class types):表示类的类型,包括实例成员、静态成员和构造函数的类型。 8. 泛型类型(Generic types):表示可以在多种类型之间共享的类型,以增加类型的灵活性和通用性。 9. 交叉类型(Intersection types):表示多个类型的合并,新类型将拥有所有类型的成员。 10. 联合类型Union types):表示两个或多个类型中的任意一个,可以使用|符号连接多个类型。 11. 类型别名(Type aliases):用于为复杂类型联合类型定义别名,以提高代码的可读性和可维护性。 12. 类型推断(Type inference):TypeScript可以自动推断变量的类型,根据变量的值确定变量的类型。 以上是TypeScript中常用的类型,开发者可以根据需要选择合适的类型来编写类型安全的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值