[译]如何在 Typescript 中实现通用的 Valueof<T> 辅助泛型
- 原文地址: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> 辅助泛型相关推荐
- 如何在TypeScript中删除数组项?
本文翻译自:How do I remove an array item in TypeScript? I have an array that I've created in TypeScript a ...
- typescript中函数_如何在TypeScript中合成Canvas动画
typescript中函数 by Changhui Xu 徐昌辉 如何在TypeScript中合成Canvas动画 (How to Compose Canvas Animations in TypeS ...
- 如何在typescript中引入jquery
引入jQuery之前的操作 在typescript中引入jQuery时首先要创建一个typescript项目(直接新建的typescript文件无法通过此方法引入) 一.创建typescript项目 ...
- 如何在typescript中移除数组中某一项
typescript中的数组 默认只有push.pop这样的增删api. 如果想移除数组中间的某一项元素, 可以通过: let node: int; //要移除的对象 nodes: int[]; t ...
- 如何在TypeScript中使用JS类库
使用流程 1.首先要清除类库是什么类型,不同的类库有不同的使用方式 2.寻找声明文件 JS类库一般有三类:全局类库.模块类库.UMD库.例如,jQuery是一种UMD库,既可以通过全局方式来引用,也可 ...
- 如何在typescript中使用axios来封装一个HttpClient类
我们通常开始直接在代码中使用像axios这样的第三方库.这没有错.但是,在不断变化的库,软件包,版本等世界中,直接使用这些库API可能会导致代码不一致. 一个好的做法是创建自己的抽象并将对库API的调 ...
- python如何读取数据集_如何在Python中读取通用数据格式(CDF)
@miraculixx的 answer是正确的,但它假定您已经安装了 CDF C Library. 如果您在SO上发现此问题之前甚至不知道CDF文件格式是什么,这是一个易于遵循的指南. 1.下载最新版 ...
- [译]如何在C#中调试LINQ查询
LINQ是我在C#中最喜欢的功能之一.它让代码看起来更漂亮美观.我们得到了一个易于编写和理解的简洁函数式语法.好吧,至少我们可以使用LINQ方法的语法风格. LINQ很难进行调试.我们无法知道该查询内 ...
- 在html5中能使用dom么,html5 – 如何在TypeScript中使用DOMStringMap?
假设我有一个功能: angular.forEach(myElements, function prepareElements(myEl: HTMLElement, index) { myEl.data ...
最新文章
- Beta 冲刺(4/7)
- mysql gbk支持_mysql支持gbk
- 网络共享服务(三)之SAMBA
- 开启3D硬件加速导致Virtualbox无法响应
- Codeforces Round #635 (Div. 2) D. Xenia and Colorful Gems 暴力 + 二分
- Leetcode--329. 矩阵中的最长递增路径
- 统一建模语言 UML
- 收银怎样挂单和取单_挂单取单(PC收银)
- 中国城市群产业建设风险与投资发展决策建议报告2022版
- java http请求工具类全功能(get、put、delete、post、文件上传),使用easy-okhttp
- 浅谈腾讯云IM接入方式(java后端)
- pytorch基础(九)- 自定义数据集训练模型 和 迁移学习
- x265 HEVC编码器,基于x264 介绍
- 从字节跳动提前批来看今年校招形势
- 格林纳达常驻WTO大使孙宇晨会见法国驻联合国日内瓦办事处代表
- java毕业设计软件源代码SSM家庭理财|个人理财管理系统|记账系统
- 基于AGS JS开发自定义贴图图层
- Facebook在美上线相亲功能,微软AI成为麻将冠军!...|一周热闻回顾
- HTML学习之四CSS盒子
- imx6 添加matrix keypad
热门文章
- 三星Q90R全景声回音壁评测(首发)转自avforums
- TPS60403DBVR 德州TI 具有固定 250kHz 操作的 60mA 电荷泵电压反向器
- Java开发微博粉丝服务(1)——环境的搭建,开发接入与URL有效性验证
- JAVA经典面试题(http://blog.csdn.net/chow__zh/article/details/7723977)
- 教程-使用js脚本预加载为网站提高访问速度
- 华硕K40in升级10.9指导
- 为毛你深陷故障驱动式开发
- Softethervpn去除开源版本的企业限制
- 使用电脑要注意护眼 -- 蓝色彼岸
- 高质量发展指标构建:全国各省高质量发展需求(2014-2021年)