TypeScript 中如何限制对象键名的取值范围

当我们使用 TypeScript 时,我们想利用它提供的类型系统限制代码的方方面面,对象的键值,也不例外。

譬如我们有个对象存储每个年级的人名,类型大概长这样:

type Students = Record<string, string[]>;

理所当然地,数据就是长这样:

const students: Students = {
  Freshman: ["David", "John"],
  sophomore: [],
  Junior: ["Lily"],
  Senior: ["Tom"],
};

限制对象键名为枚举

上面数据类型的问题是,年级是有限的几种可值取,而该对象上可任意添加属性,这样显得数据不够纯粹。

所以我们新增枚举,列出可取的值:

export enum Grade {
  Freshman,
  sophomore,
  Junior,
  Senior,
}

现在,把对象的键名限制为上面枚举就行了。

- type Students = Record<string, string[]>;
+ type Students = Record<Grade, string[]>;

这样我们的数据可写成这样:

const students: Students = {
  [Grade.Freshman]: ["David", "John"],
  [Grade.sophomore]: [],
  [Grade.Junior]: ["Lily"],
  [Grade.Senior]: ["Tom"],
  // ❌ Object literal may only specify known properties, and 'blah' does not exist in type 'Students'.ts(2322)
  blah: ["some one"],
};

这样,限制住了对象身上键名的范围,可以看到如果添加一个枚举之外的键会报错。

更加语义化的枚举值

但上面的做法还是有不妥之处,因为枚举值默认是从 0 开始的数字,这样,作为键值就不够语义了,这点从访问对象的属性时体现了出来:

修正我们的枚举,用更加语义的文本作为其值:

export enum Grade {
  Freshman = "Freshman",
  sophomore = "sophomore",
  Junior = "Junior",
  Senior = "Senior",
}

此时再使用该枚举时,得到的就不是无意义的数字了。

如果你愿意,枚举值也可以是中文,

export enum Grade {
  Freshman = "大一萌新",
  sophomore = "大二学弟",
  Junior = "大三学妹",
  Senior = "大四老司机",
}

使用时也是没任何问题的:

键值可选

上面的类型定义还有个问题,即,它要求使用时对象包含枚举中所有值,比如 sophomore 这个年级中并没有人,可以不写,但会报错。

// ❌ Property 'sophomore' is missing in type '{ Freshman: string[]; Junior: string[]; Senior: string[]; }' but required in type 'Students'.ts(2741)
const students: Students = {
  [Grade.Freshman]: ["David", "John"],
  // [Grade.sophomore]: [],
  [Grade.Junior]: ["Lily"],
  [Grade.Senior]: ["Tom"],
};

所以,优化类型为可选:

type Students = Partial<Record<Grade, string[]>>;

假若可选的值不是通过枚举定义,而是来自一个数组,

const grades = ["Freshman", "sophomore", "Junior", "Senior"];

这意味着我们需要提取数组中的值形成一个联合类型。

首先利用const assertions 把数组转元组(Tuple)类型,

const grades = <const>["Freshman", "sophomore", "Junior", "Senior"];

再利用 typeofLookup Types 得到最终的联合类型:

// 实际为 type Keys = "Freshman" | "sophomore" | "Junior" | "Senior"
type Keys = typeof grades[number];

最后数据类型和数据可写成:

type Students = Partial<Record<Keys, string[]>>;

const students: Students = {
  Freshman: ["David", "John"],
  Junior: ["Lily"],
  Senior: ["Tom"],
};

须知这种形式下,对象的 key 与原数组中元素其实没有语法层面的关联,即,编辑器的「跳转定义」是不可用的。

尽量还是保持代码之间的关联才能体现出 TypeScript 的作用,所以像这种只有类型约束而无法建立关联的操作是不建议的。

相关资源

const assertions

keyof and Lookup Types

The text was updated successfully, but these errors were encountered:

以上就是TypeScript 中限制对象键名的取值范围的详细内容,更多关于TypeScript限制对象键名范围的资料请关注我们其它相关文章!

(0)

相关推荐

  • 一文搞懂TypeScript的安装、使用、自动编译的教程

    1. 初识 TypeScript 上篇文章给大家介绍过TypeScript的安装.使用.自动编译的实现  需要的朋友点击查看. TypeScript 的介绍 TypeScript 是一种由微软开发的开源.跨平台的编程语言.它是 JavaScript 的超集,最终会被编译为 JavaScript 代码. 2012 年 10 月,微软发布了首个公开版本的 TypeScript,2013 年 6 月 19 日,在经历了一个预览版之后微软正式发布了正式版 TypeScript TypeScript 的作

  • 为什么TypeScript的Enum会出现问题

    目录 发生了什么呢? 什么时候用 控制枚举的数字 Bit值 控制索引 非数字枚举 结论 TypeScript引入了很多静态编译语言的特性,比如class(现在是JavaScript的一部分了),interface, generics和union types等. 但是今天有一个类型需要着重讨论下,这就是enum. 对于很多的静态语言来说,枚举是一个很非常常见的语言特性.比如,c,c#,java和swift.枚举就是你在代码里可以用的一组常量. 我们用TypeScript来新建一个enum来代表一周

  • 深入理解typescript中的infer关键字的使用

    infer 这个关键字,整理记录一下,避免后面忘记了.有点难以理解呢. infer infer是在 typescript 2.8中新增的关键字. infer 可以在 extends 条件类型的字句中,在真实分支中引用此推断类型变量,推断待推断的类型. 例如:用infer推断函数的返回值类型 type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; type fn = () => number type

  • Typescript中的as、问号与感叹号详解

    1.as关键字表示断言 在Typescript中,表示断言有两种方式.一种是扩号表示法: let someValue: any = "this is a string"; let strLength: number = (someValue).length; 另一种使用as关键字: let someValue: any = "this is a string"; let strLength: number = (someValue as string).length

  • 教你使用webpack打包编译TypeScript代码

    TypeScript打包 webpack整合 通常情况下,实际开发中我们都需要使用构建工具对代码进行打包: TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS: 步骤如下: 初始化项目 进入项目根目录,执行命令 npm init -y,创建package.json文件 下载构建工具 命令如下: npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpac

  • 详解TypeScript映射类型和更好的字面量类型推断

    概述 TypeScript 2.1 引入了映射类型,这是对类型系统的一个强大的补充.本质上,映射类型允许w咱们通过映射属性类型从现有类型创建新类型.根据咱们指定的规则转换现有类型的每个属性.转换后的属性组成新的类型. 使用映射类型,可以捕获类型系统中类似Object.freeze()等方法的效果.冻结对象后,就不能再添加.更改或删除其中的属性.来看看如何在不使用映射类型的情况下在类型系统中对其进行编码: interface Point { x: number; y: number; } inte

  • TypeScript 中如何限制对象键名的取值范围

    当我们使用 TypeScript 时,我们想利用它提供的类型系统限制代码的方方面面,对象的键值,也不例外. 譬如我们有个对象存储每个年级的人名,类型大概长这样: type Students = Record<string, string[]>; 理所当然地,数据就是长这样: const students: Students = { Freshman: ["David", "John"], sophomore: [], Junior: ["Lil

  • JS中的JSON对象的定义和取值实现代码

    1.JSON(JavaScript Object Notation)一种简单的数据格式,比xml更轻巧.JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不需要任何特殊的API或工具包. JSON的规则很简单:对象是一个无序的"'名称:值'对"集合.一个对象以"{"(左括号)开始,"}"(右括号)结束.每个"名称"后跟一个":"(冒号):"'名称/值'对&qu

  • 微信小程序开发之改变data中数组或对象的某一属性值

    前言:在小程序的开发中,我们在view中便利data中数组或对象时,很多情况下需要在js中动态改变数组或者对象中某一香的属性值. 效果图: 我给大家总结了案例如下: wxml如下: <scroll-view class='hd-cont' scroll-x="true" style='width: 100%'> <view wx:for="{{sDate}}" wx:key="{{index}}" class='date-con

  • SpringMVC中Model和ModelAndView的EL表达式取值方法

    model和modelMap(spring 封装),Java.util.Map ModelMap(视图) ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", "xxx"); modelAndView.setViewName("/user/index"); return modelAndView; //对于ModelAndView构造函数可以指

  • MyBatis中foreach标签的collection属性的取值方式

    目录 foreach标签的collection属性的取值 传的是List列表 传的是Array数组 传的是Map collection属性总结 MyBatis使用foreach标签报错 原因 解决方案 foreach标签的collection属性的取值 传的是List列表 接口代码 List<Emp> findEmpByDeptnos(List<Integer> deptnos); xml配置代码 <select id="findEmpByDeptnos"

  • 解析数组非数字键名引号的必要性

    我看到过很多人操作数组的时候, 对于数组中的非数字键名不使用引号 复制代码 代码如下: $array[key] = $value; 我可以理解有些人可能会觉得这样的代码很"整洁", 并且也能正常执行.更甚至,如果他很"幸运的"php配置的好: 复制代码 代码如下: error_reporting = ~E_NOTIC 他也许永远都沉浸在自己的"整洁"风格中, 看不到任何的NOTICE提示, 也不会意识到, 他这么做, 能损失多少的性能~来, 我们

  • php数组函数序列之array_flip() 将数组键名与值对调

    array_flip() 定义和用法 array_flip() 函数将使数组的键名与其相应值调换,即键名变成了值,而值变成了键名. array_flip() 函数返回一个反转后的数组,如果同一值出现了多次,则最后一个键名将作为它的值,所有其他的键名都将丢失. 如果原数组中的值的数据类型不是字符串或整数,函数将报错. 语法 array_flip(array)参数 描述 array 必需.规定输入的数组. 例子 复制代码 代码如下: <?php $a=array(0=>"Dog"

  • PHP去除空数组且数组键名重置的讲解

    php空数组的话,能够运用 php函数array_filter() . array array_filter ( array [, callback callback] ) array_filter() 顺次将 array 数组中的每个值传递到 callback 函数.假如 callback 函数回来 TRUE,则 array 数组的当时值会被包含在回来的成果数组中.数组的键名保存不变. 假如没有供给 callback 函数,array_filter() 将删去 array 中所有等值为 FAL

  • Java中Map集合中的Entry对象用法

    Entry: 键值对 对象. 在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry.Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值. Entry为什么是静态的? Entry是Map接口中提供的一个静态内部嵌套接口,修饰为静态可以通过类名调用. Map集合遍历键值对的方式: Set<Map.Entry<K,V>> entrySet(); //返回此映射中包含的映射

  • 详解pandas中利用DataFrame对象的.loc[]、.iloc[]方法抽取数据

    pandas的DataFrame对象,本质上是二维矩阵,跟常规二维矩阵的差别在于前者额外指定了每一行和每一列的名称.这样内部数据抽取既可以用"行列名称(对应.loc[]方法)",也可以用"矩阵下标(对应.iloc[]方法)"两种方式进行. 下面具体说明: (以下程序均在Jupyter notebook中进行,部分语句的print()函数省略) 首先生成一个DataFrame对象: import pandas as pd score = [[34,67,87],[68

随机推荐