• 原文地址:How to implement a generic ValueOf helper type in TypeScript.
  • 原文作者:Stephan Meijer

Sudhanshu 昨天在KCD Discord上问了一个关于 Typescrpit 有趣的问题:

是否可以将变量的类型限制为普通对象的值?

我提供了解决方案,但他想了解其中的原理,所以我就顺手写了这篇文章来与各位分享这点知识。

我们先从纯 Javascript 代码开始,看一个符合 Sudhanshu 要求的在运行时执行校验的例子

const SHAPES = {SQUARE: 'square',CIRCLE: 'circle',
};const value = 'square';// 校验 value 是否是 SHAPES 的 values 之一
const validValues = Object.values(SHAPES);
const isValid = validValues.includes(value);if (!isValid) {throw new TypeError(`'value' should be one of: ${validValues.join(' | ')}`);
}

以上代码将会在 value 不等于 square 或 circle 时抛出错误。运行时校验挺好的,但问题是这些工作是否可以通过 Typescrpit 在静态阶段完成。

哈哈,这当然可以啦!

限制对象的值

我们面临的第一个挑战是,我们在操作一个对象(object)而不是一个类型(type)。

所以,我们应该先从对象提取出类型,那么就该使用 typeof

const SHAPES = {SQUARE: 'square',CIRCLE: 'circle',
};type Shape = typeof SHAPES;

现在类型 Shape 就是

type Shape = { SQUARE: string;CIRCLE: string;
}

但这还不是我想要的。

如果我们需要验证 value 是否包含在对象 SHAPES 的值(square | circle)中,我们当然需要验证。我们可以通过关键字as const将对象声明为常量,加上该申明后,我们向 Typescript 保证我们不会在运行时改变这一对象,Typescript 会将它视为一种类似于枚举类型的对象。

const SHAPES = {SQUARE: 'square',CIRCLE: 'circle',
} as const;

加了as const后,类型 Shape 就变为

type Shape = { readonly SQUARE: 'square';readonly CIRCLE: 'circle';
}

解释一下这里发生了什么:

  • 首先,所有属性都被标记为只读readonly,此后再修改该对象都将会收到来自 typescript 的错误提示
  • 其次,现在各属性都被限制为它们对应的枚举值,而不是之前的 string 类型。

有了它之后,我们就得到了一个可以使用的类型了。Typescript 并没有 valueof 这样的辅助类型,但我们知道它有一个 keyof,看看它能不能帮到我们。

type keys = keyof Shape;

keyof 从以上类型 Shape 中获取它所有键的联合类型,所以类型 keys 就等同于:

type keys = 'SQUARE' | 'CIRCLE';

有了键,那么我们就能获取到值了。其实或许到这,你可能就已经知道该如何提取值并复用它了。

就比如,如果你想得到 SQUARE 的类型,那么你可以:

type Square = Shape['SQUARE']; // square

现在,你可以基于以上Square类型声明一个新的联合类型,大家应该会这么写:

type ValidShapes = Shape['SQUARE'] | Shape['CIRCLE']; // square | circle

当然,你也可以改为这样更简短的方式:

type ValidShapes = Shape['SQUARE' | 'CIRCLE']; // square | circle

我们来总结一下。以上我们使用keyof来获取反映Shpae键的联合类型,以及一个比较简捷的从值创建联合类型的方法。现在我们再回头看以上最后一个代码片段,你会发现索引参数不过是一个联合类型。其实,其实我们可以直接将keyof丢在里面。

汇总一下以上关键代码片段:

// 使用 as const 将对象声明为常量,因此 ts 将其识别为枚举类型
const SHAPES = {SQUARE: 'square',CIRCLE: 'circle',
} as const;// 从对象 SHAPES 中创建一个类型
type Shape = typeof SHAPES;// 从对象的键 (SQUARE | CIRCLE) 创建一个联合类型
type Shapes = keyof Shape;// 从对象的值 (square | circle) 创建一个联合类型
type Values = Shape[Shapes];

然后我们就可以用类型 Values 来限定属性的类型了。

const shape: Values = 'circle';

当我们尝试给类型Values限定的变量赋除 square 或 circle 之外的任何值时,typescript 都将会抛出错误。

那么,既然完成任务,今天的文章就结束啦!

多提一句,虽然运行时校验真的很棒,但是现在我们已经不再需要它了,因为如果我们赋了一个不被支持的值时,甚至都不能通过编译

[译]如何在 Typescript 中实现通用的 Valueof<T> 辅助泛型相关推荐

  1. 如何在TypeScript中删除数组项?

    本文翻译自:How do I remove an array item in TypeScript? I have an array that I've created in TypeScript a ...

  2. typescript中函数_如何在TypeScript中合成Canvas动画

    typescript中函数 by Changhui Xu 徐昌辉 如何在TypeScript中合成Canvas动画 (How to Compose Canvas Animations in TypeS ...

  3. 如何在typescript中引入jquery

    引入jQuery之前的操作 在typescript中引入jQuery时首先要创建一个typescript项目(直接新建的typescript文件无法通过此方法引入) 一.创建typescript项目 ...

  4. 如何在typescript中移除数组中某一项

    typescript中的数组 默认只有push.pop这样的增删api. 如果想移除数组中间的某一项元素, 可以通过: let node: int;  //要移除的对象 nodes: int[]; t ...

  5. 如何在TypeScript中使用JS类库

    使用流程 1.首先要清除类库是什么类型,不同的类库有不同的使用方式 2.寻找声明文件 JS类库一般有三类:全局类库.模块类库.UMD库.例如,jQuery是一种UMD库,既可以通过全局方式来引用,也可 ...

  6. 如何在typescript中使用axios来封装一个HttpClient类

    我们通常开始直接在代码中使用像axios这样的第三方库.这没有错.但是,在不断变化的库,软件包,版本等世界中,直接使用这些库API可能会导致代码不一致. 一个好的做法是创建自己的抽象并将对库API的调 ...

  7. python如何读取数据集_如何在Python中读取通用数据格式(CDF)

    @miraculixx的 answer是正确的,但它假定您已经安装了 CDF C Library. 如果您在SO上发现此问题之前甚至不知道CDF文件格式是什么,这是一个易于遵循的指南. 1.下载最新版 ...

  8. [译]如何在C#中调试LINQ查询

    LINQ是我在C#中最喜欢的功能之一.它让代码看起来更漂亮美观.我们得到了一个易于编写和理解的简洁函数式语法.好吧,至少我们可以使用LINQ方法的语法风格. LINQ很难进行调试.我们无法知道该查询内 ...

  9. 在html5中能使用dom么,html5 – 如何在TypeScript中使用DOMStringMap?

    假设我有一个功能: angular.forEach(myElements, function prepareElements(myEl: HTMLElement, index) { myEl.data ...

最新文章

  1. Beta 冲刺(4/7)
  2. mysql gbk支持_mysql支持gbk
  3. 网络共享服务(三)之SAMBA
  4. 开启3D硬件加速导致Virtualbox无法响应
  5. Codeforces Round #635 (Div. 2) D. Xenia and Colorful Gems 暴力 + 二分
  6. Leetcode--329. 矩阵中的最长递增路径
  7. 统一建模语言 UML
  8. 收银怎样挂单和取单_挂单取单(PC收银)
  9. 中国城市群产业建设风险与投资发展决策建议报告2022版
  10. java http请求工具类全功能(get、put、delete、post、文件上传),使用easy-okhttp
  11. 浅谈腾讯云IM接入方式(java后端)
  12. pytorch基础(九)- 自定义数据集训练模型 和 迁移学习
  13. x265 HEVC编码器,基于x264 介绍
  14. 从字节跳动提前批来看今年校招形势
  15. 格林纳达常驻WTO大使孙宇晨会见法国驻联合国日内瓦办事处代表
  16. java毕业设计软件源代码SSM家庭理财|个人理财管理系统|记账系统
  17. 基于AGS JS开发自定义贴图图层
  18. Facebook在美上线相亲功能,微软AI成为麻将冠军!...|一周热闻回顾
  19. HTML学习之四CSS盒子
  20. imx6 添加matrix keypad

热门文章

  1. 三星Q90R全景声回音壁评测(首发)转自avforums
  2. TPS60403DBVR 德州TI 具有固定 250kHz 操作的 60mA 电荷泵电压反向器
  3. Java开发微博粉丝服务(1)——环境的搭建,开发接入与URL有效性验证
  4. JAVA经典面试题(http://blog.csdn.net/chow__zh/article/details/7723977)
  5. 教程-使用js脚本预加载为网站提高访问速度
  6. 华硕K40in升级10.9指导
  7. 为毛你深陷故障驱动式开发
  8. Softethervpn去除开源版本的企业限制
  9. 使用电脑要注意护眼 -- 蓝色彼岸
  10. 高质量发展指标构建:全国各省高质量发展需求(2014-2021年)