TypeScript开发小状况记录之选且只选一个

目录
  • 前言
  • 背景
  • 初步方案
  • 终极方案
  • 总结

前言

在项目内,使用TypeScript的过程中遇到了一些状况、报错或棘手的情况,决定开一个系列记录遇到的问题和我的解决方法。

背景

在项目开发中,很多时候会遇到一种场景,需要定义一个对象的类型,此类型必须包含某n个字段中的其中一种。

例如,我要定义一个工程师(Engineer)的对象,里面包括姓名(name),性别(gender),年龄(age)和一门编程语言(java/cpp/go/js四选一)的评价。

显然,前三个字段都是很简单的,但是第四个就有点麻烦了。首先,第四个字段的key是可以不一样(甚至value也有可能不同),其次字段只能从给定的里面4选1。

初步方案

一开始是考虑使用可选或联合类型,但是发现没有办法进行4选1的限制,对于没有编程语言字段,或者多个编程语言字段的情况并没有很好的限制。最后只能使用泛型,再使用时进行显式的声明。

于是,类型定义如下:

interface ICodingLangRating {
    java: string
    cpp: string
    go: string
    js: string
}

type Engineer<K extends keyof ICodingLangRating> = {
    name: string
    gender: 'male' | 'female'
    age: number
} & Pick<ICodingLangRating, K>

对该声明的校验代码如下:

// 正确
const candidate: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    java: 'fabulous'
}

// 错误,声明了java,但是却同时定义了java和go字段
const candidate_1: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    java: 'fabulous',
    go: 'not bad'
}

// 错误,声明了java,但是类型不正确
const candidate_2: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    java: 666
}

// 错误,声明了java,但是却定义了go字段
const candidate_3: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    go: 'not bad'
}

// 错误,声明了java,但是却同时定义了cpp和go字段
const candidate_4: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    cpp: 'unknown',
    go: 'not bad'
}

// 错误,声明了java,但是却没有定义java字段
const candidate_5: Engineer<'java'> = {
    name: 'Jack',
    gender: 'male',
    age: 22
}

// 错误,声明了ICodingLangRating中不存在的python
const candidate_6: Engineer<'python'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    python: 'just so so'
}

从校验代码可以看出,针对各种不符合期望的情况:

  • 声明a,却定义a和b
  • 声明a,但定义a的类型不正确
  • 声明a,却定义b
  • 声明a,却定义b和c
  • 声明a,却没有定义a
  • 声明不合法的f

都能做出正确的限制,确保在业务场景的代码中,有且只有一个合法范围的字段。

但是,转折来了!

在后来的使用中,我们发现,其实这个解决方案只是一个弱限制,如果在泛型的显式声明中,传入联合类型的话,那还是可以绕过有且只有一个编程语言字段的限制。

// 正确,声明了java和go,并同时定义了java和go字段
const candidate_1: Engineer<'java' | 'go'> = {
    name: 'Jack',
    gender: 'male',
    age: 22,
    java: 'fabulous',
    go: 'not bad'
}

难道就真的没有办法做到只能选择一个的限制么?

终极方案

根据上面的尝试,目前我们还缺少的是如何阻止同时有2个或以上的合法字段出现。最笨的方式就是为每一个语言都定义一个类似{ langName: string }这样的类型然后通过extends或者联合类型使用,但是显然这样就没有办法做到在其它情况通用。

而通过官方现成的工具类型,由于都是支持字面量和联合类型,没有办法筛选出只包含一个字段的类型。就在这时,我想到,是不是可以定义出一个类型,包含全部字段,但是只有一个字段是正确有意义,其他字段都是无意义的呢。

最终,我就构造出下面这个PickOne工具类型:

type PickOne<T> = {
    [K in keyof T]: Record<K, T[K]> & Partial<Record<Exclude<keyof T, K>, undefined>>
}[keyof T]

测试代码如下:

type OneLang = PickOne<ICodingLangRating>

// 正确
const lang: OneLang = {
    java: 'good'
}

// 错误
const lang2: OneLang = {
    python: 'unknown'
}

// 错误
const lang3: OneLang = {
    java: 'good',
    go: 'good'
}

// 错误
const lang4: OneLang = {
    java: 123
}

最后,类型定义代码如下:

interface ICodingLangRating {
    java: string
    cpp: string
    go: string
    js: string
}

type Engineer = {
    name: string
    gender: 'male' | 'female'
    age: number
} & PickOne<ICodingLangRating>

使用了这个PickOne工具类型,我不需要在使用的时候显式的指定编程语言,甚至还能在其它类似的场景使用。

总结

到此这篇关于TypeScript开发小状况记录之选且只选一个的文章就介绍到这了,更多相关TypeScript选且只选一个内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • TypeScript开发小状况记录之选且只选一个

    目录 前言 背景 初步方案 终极方案 总结 前言 在项目内,使用TypeScript的过程中遇到了一些状况.报错或棘手的情况,决定开一个系列记录遇到的问题和我的解决方法. 背景 在项目开发中,很多时候会遇到一种场景,需要定义一个对象的类型,此类型必须包含某n个字段中的其中一种. 例如,我要定义一个工程师(Engineer)的对象,里面包括姓名(name),性别(gender),年龄(age)和一门编程语言(java/cpp/go/js四选一)的评价. 显然,前三个字段都是很简单的,但是第四个就有

  • 关于TypeScript开发的6六个实用小技巧分享

    目录 1. 开发之前确定实体类型 2. 请求接口时只需要定义自己需要用到的字段 3. 使用枚举类型 4. DOM元素的类型要正常给 5.对象的类型要怎么给 6.结构赋值时类型怎么给 总结 本文总结一下使用TypeScript开发应用程序的一点小经验.说之前,推荐一个VSCODE立即执行TS代码的插件quokka.js, 使用方式,ctrl+shipt+p,输入关键字quokka 回车之后,输入代码之后会立即执行 1. 开发之前确定实体类型 在正式编码之前,如果没有接口文档的话,最好能拿到数据字典

  • JQUERY CHECKBOX全选,取消全选,反选方法三

    jquery.checkboxes.zip,然后解压引用到使用插件的页面,这个不用多说了吧!下面看具体例子,为了让大家更好的理解,我直接把实现功能的代码贴出来: 复制代码 代码如下: $("#myform").toggleCheckboxes()//全选,取消全选,反选 $("#myform").toggleCheckboxes(":not(#checkbox1)")//全选,取消全选且不选中第一个,反选 $("#myform"

  • 使用TypeScript开发微信小程序的方法

    TypeScript简介: TypeScript是一种由微软开发的自由和开源的编程语言.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程.安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发. TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作.TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性.

  • 使用Typescript开发微信小程序的步骤详解

    Typescript的优势咱不需要赘述太多,有兴趣可以参考(https://www.typescriptlang.org/).今天给大家分享一下如何在微信小程序(或者其他同类小程序)开发中使用Typescript. 这个分两种情况,最简单的做法就是在创建项目时,选择Typescript这个选项,如下图所示.但要注意,这个选项只有在选择"Use no cloud service"才有,而另外一种Mini Program Cloud Base则不支持.这个可能是开发工具还没有跟上吧,希望以

  • 详解使用Typescript开发node.js项目(简单的环境配置)

    最近在学习typescript的过程中,想到也许可以使用ts来开发node.js项目.在网上搜了一下,其实已经有很多开发者实践了这方面的内容.这里,我记录一下自己搭建开发环境的简单过程. 使用Typescript开发的好处: 较严格的类型检查和语法检查. 对ES6/ES2015/ES7(部分)支持比较好. 编译后的js文件很干净,也支持多种代码规范. 其他,请参见文档. 准备 node.js v6.9.1 或者任意的新版本,老版本暂时没有试验. tsc typescript编译器,使用npm安装

  • android基于ListView和CheckBox实现多选和全选记录的功能

    应用开发中经常会有从数据库中读取数据显示,然后选中多条.全部记录并且删除的需求.在做定制系统联系人的时候也遇到这样的需求,下面写个简单的通过ListView和CheckBox实现多选.全选的例子.下面是具体的代码. 效果如下: MultiSelectActivity /** * MultiSelectActivity */ public class MultiSelectActivity extends Activity implements OnClickListener, OnItemCli

  • 微信小程序实现多选框全选与取消全选功能示例

    本文实例讲述了微信小程序实现多选框全选与取消全选功能.分享给大家供大家参考,具体如下: js部分: page({ data: { select_all:false, listData: [{code: "111",text: "text1",typ: "type1",}, {code: "021",text: "text2",typ: "type2",}, {code: "11

  • 详解如何用typescript开发koa2的二三事

    前言 最近在写一个博客的项目,前端用的 vue+typescript+element-ui ,后台则选择了 koa2+typescript+mongoDB 的组合.写这篇博客的目的也是在写后台的过程遇到一些问题,查了很多资料才解决.于是权当总结,亦是记录,可以给别人做一个完整的参考. 基本信息 这里列出来的是会用到的一些配置信息,毕竟一直都在更新,可能这里说的以后某个版本就不支持了. "nodemon" : "^1.18.3", "ts-node"

  • Taro UI框架开发小程序实现左滑喜欢右滑不喜欢效果

    Taro 就是可以用 React 语法写小程序的框架,拥有多端转换能力,一套代码可编译为微信小程序.百度小程序.支付宝小程序.H5.RN等 摘要: 年后入职了一家新公司,与前同事交接完之后,发现公司有一个四端的项目(iOS,Android,H5,小程序),iOS和安卓都实现了左滑右滑的效果,而h5和小程序端没实现,询问得知前同事因网上没找到对应的插件,相关博客也特别少,所以没做就搁置下来了. 趁这段时间相对来说比较富裕,于是乎在网上也搜索了一下,发现确实很少,但是有人提到可以用小程序可拖动组件m

随机推荐