Typescript的枚举和范围缩小

关于枚举

反向映射原理

以前只知道如何建枚举,如何使用枚举,但是不知道如何通过Typescript拿到枚举内容,通过keyof typeof

反向映射获取枚举key

enum Enum {
  A,
}
 
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

关于常量枚举

常量枚举的使用

  • 大多数情况下,枚举是要给完全有效的解决方案,然而,又是要求严格,为了避免在访问枚举值需要生成额外的代码。可以使用const枚举,标记为常量。
const enum Enum{
	A = 1, 
	 B = A * 2,
}

常量枚举能使用常量枚举表达式,它与常规的枚举不同,他们编译期间会被完全删除。常量枚举成员在使用站点内联。

const enum Direction {
  Up,
  Down,
  Left,
  Right,
}

let directions = [
  Direction.Up,
  Direction.Down,
  Direction.Left,
  Direction.Right,
];

//以下是上面转化的结果,内联展示
"use strict";
let directions = [
    0 /* Direction.Up */,
    1 /* Direction.Down */,
    2 /* Direction.Left */,
    3 /* Direction.Right */,
];

常量枚举的陷阱

》 内联枚举值一开始很简单,但会带来一些影响。这些陷阱仅与环境const枚举(基本上是.d.ts文件中的const枚举)以及在项目之间共享有关。但是如果你正在发布或使用.d.ts文件。这些陷阱你就要考虑了。

  • 由于isolatedModules文件中列出的原因改模式从根本上与环境常量枚举不兼容。这就意味着如果你发布环境常量枚举,饮用者将无法同时使用isolatedModules和那些枚举值。

isolatedModules(隔离模块),我们将Typescript代码生成Jacascript代码,会使用到Babel等其他转换器来执行,然而,其他转换器一次只能对单个文件进行操作,这就意味着他们不能应用依赖于丽洁完整类型操作系统的代码转换。此限制也适用于Typescript的ts.transileModule API,这些限制可能会导致一些Typescript的功能(如const枚举和命名空间)的运行时问题,设置隔离模块标志会告诉Typescript警告您,如果您编写的某些代码无法被单个文件转换过程正确解释。

上面的内容用白话文解释以下:

为什么需要设置isolatedModules为true

假设有如下两个 ts 文件,我们在 a.ts 中导出了 Test 接口,在 b.ts 中引入了 a.ts 中的 Test 接口,
然后又在 b.ts 将 Test 给导出。

// a.ts
export interface Test {}

// b.ts
import { Test } from './a';

export { Test };

这会造成一个什么问题呢,如 Babel 对 ts 转义时,它会先将 ts 的类型给删除,但是当碰到 b.ts 文件时,

Babel 并不能分析出 export { Test } 它到底导出的是一个类型还是一个实实在在的 js 方法或者变量,这时候 Babel 选择保留了 export。但是 a.ts 文件在转换时可以很容易的判定它就导出了一个类型,在转换为 js 时,a.ts 中的内容将被清空,而 b.ts 中导出的 Test 实际上是从 a.ts中引入的,这时候就会产生报错。

如何解决?

ts提供了import type or export type,用来明确标识我引入/导出的是一个类型,而不是一个变量或者方法,使用 import type 引入的类型 将在转换时js被删除掉

// b.ts
import { Test } from './a';

export type { Test }; 
  • 你可以在编译时轻松的从以来的版本A中内联值,并在运行时导入版本B,版本A和B的枚举可以有不同的值,如果你时很小心,导致了意外的缺陷,就像做错了if语句的分支。这些错误特别有害,因为通常在构建项目的同时运行自动化测试,具有相同的依赖版本,完全忽略了这些错误
  • importsNotUsedAsValues:"preserve"不会忽略用作值的const枚举的导入,但环境const枚举不保证运行时.js文件存在。无法解析的导入会在运行时导致错误。目前明确省略导入的常见方法,仅类型导入,不允许const枚举值

如何避免陷阱

  • 根本不使用常量枚举,可以通过配置eslint[禁用const枚举](故障排除和常见问题解答 | typescript-eslint 中文网),显然,这避免了const枚举的任何问题,但会阻止你的项目内联自己的枚举。与其他项目的内联枚举不同,内联项目自己的枚举是没有问题的。并不会影响性能。
  • 不要通过在preserveConstEnums的帮助下构建来发布环境常量枚举。这是Typescript项目本身内部采用的方法,preserveConstEnums为const枚举触发与普通枚举相同的Javascript,然后,你可以安全的从.d.ts文件在构建不种种删除const修饰符。

环境枚举

环境枚举用于描述已经存在的枚举类型的形状

declare enum Enum {
  A = 1,
  B,
  C = 2,
}

环境枚举和非环境枚举之间的一个重要区别是,在常规枚举中,如果之前的枚举成员被认为是常量,那么没有初始化器的成员将被认为是常量。 相比之下,没有初始值设定项的环境(和非常量)枚举成员始终被视为已计算。

对象枚举

const enum EDirection {
  Up,
  Down,
  Left,
  Right,
}
 
const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;

关于类型缩小

使用typeof

可以使用typeof运算符,提供我们关于我们在运行时拥有的值类型的基本信息

  • "string"
  • "number"
  • "bigint"
  • "boolean"
  • "symbol"
  • "undefined"
  • "object"
  • "function"

需要注意:由于Javascript中的一些怪癖,例如typeof不返回字符串null,typeof null 结果时object

&&、||、if、!

if判断通过以下是否返回true或者false,这里需要注意undefined和null

  • 0
  • NaN
  • "" (空字符串)
  • 0n (bigint 版本零)
  • null
  • undefined

switch、===、!==、==、!=

TypeScript 还使用 switch 语句和 ===、!==、== 和 != 等相等性检查来缩小类型。 例如:

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

使用in运算符

type Fish = { swim: () => void };
type Bird = { fly: () => void };
 
function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
  }
 
  return animal.fly();
}

instanceof 缩小

JavaScript 有一个运算符用于检查一个值是否是另一个值的 “instance”。 更具体地说,在 JavaScript 中,x instanceof Foo 检查 x 的原型链是否包含 Foo.prototype。 虽然我们不会在这里深入探讨,并且当我们进入类时你会看到更多内容,但它们对于可以使用 new 构造的大多数值仍然很有用。 你可能已经猜到了,instanceof 也是一个类型保护,TypeScript 缩小了由 instanceof 保护的分支。

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

使用类型谓词is

要定义用户定义的类型保护,我们只需要定义一个返回类型为类型谓词的函数

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}


//调用
// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();
 
if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

使用断言

使用断言函数缩小类型 as

判别联合

interface Shape {
  kind: "circle" | "square";
  radius?: number;
  sideLength?: number;
}

//我们一般的写法都是 
function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;
'shape.radius' is possibly 'undefined'.
  }
}

上面是我一般的写法,这个时候就会遇到rect是非必填的字段,这个时候我们就会遇到报错,一般情况下我都是用下面的方法进行更改

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

这次发现,上面的方法使用了非空断言(!)其实并不友好,避免减少断言的使用减少代码出错。我们可以从新规划下的我们的接口

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;
                      
(parameter) shape: Circle
  }
}

//注意如果为了代码清楚判断较多的情况可以直接使用switch

never类型

可以使用never类型来表示不应该存在的状态,never类型可以分配给每一个类型,但是,没有类型可分配给never(never本身除外)。这就意味着你可以使用缩小范围并依靠出现的never在switch语句中进行详尽检查

例如,将 default 添加到我们的 getArea 函数中,尝试将形状分配给 never,当处理完所有可能的情况时,不会引发错误。

type Shape = Circle | Square;
 
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值