TypeScript:带属性关联的泛型对象解构问题研究

简介: ## 背景 ### 利用泛型进行属性关联 大家在业务中一定很熟悉这样的场景,针对某个action,传递一个指定类型的payload。 有了ts之后,我们会期望用泛型将action和payload的对应关系约束起来。 例如下面这段demo ``` typescript type Payloads = { people: { name: string, age: number };

背景

利用泛型进行属性关联

大家在业务中一定很熟悉这样的场景,针对某个action,传递一个指定类型的payload。
有了ts之后,我们会期望用泛型将action和payload的对应关系约束起来。
例如下面这段demo

type Payloads = {
  people: { name: string, age: number };
  machine: { id: string, price: number };
}
type Keys = keyof Payloads;

type Foo<T extends Keys, P> = {
  key: T;
  payload: P;
  info: string;
};
type AllFoos = { [K in Keys]: Foo<K, Payloads[K]> };
type AnyFoo = AllFoos[Keys];

11cb5d912244cc3374892f9e3991b021.png
这里我首先定义了action和payload结构关联的map,然后创建类型ALLFoos,利用泛型对象将action的key和对应的payload结构关联起来。最后输出的AnyFoo类型就是所有有效Foo类型的联合类型。

现在我就可以利用ts的类型检查,确保key和payload的关系约束了。

const makeFoo = (key: Keys): AnyFoo => {
  if (key === 'people') {
    return {
      key,
      payload: { name: 'xinyuehtx', age: 18 },
      info: '帅'
    }
  } else {
    return {
      key,
      payload: { id: '终结者', price: 998 },
      info: '莫得感情'
    }
  }
}

如果我们将“终结者”的属性名称改成“name”,ts就会帮我们报错
image.png

属性关联类型解构

那么现在问题来了,假如我要新增一个对外开放的API,要从Foo这个类型中去掉info属性,但是又不能改变Foo现有的结构。这个需求常见于内部方法对外开放时,去除一些隐私信息。毕竟面对第三方,˙终结者不希望别人知道他是一个莫得感情的机器,我也不希望别人发现我很帅。

第一时间我想到的方案如下,建立一个Bar类型,定义如Foo,但是去掉了info属性。
然后从foo对象中选取特定属性进行赋值。

type Bar<T extends Keys, P> = {
  key: T;
  payload: P;
};

type AllBars = { [K in Keys]: Bar<K, Payloads[K]> };
type AnyBar = AllBars[Keys];

const makeBarFromFoo = (foo: AnyFoo): AnyBar => ({
  key: foo.key,
  payload: foo.payload,
})

image.png
但是这里ts报错了。为什么呢?在这里,ts能够识别foo.key是一个'people'|'machine'的联合类型,foo.payload也是对应payload结构的联合类型,但是他们之间的关联丢失了。
ts不能消除people对应{id,price}的可能性。

解决方案

  • 通过as强转:
    这个是最偷懒的方案,直接跳过检查,通过人工约束。可行,但是非到万不得已不要用
  • 在实现代码中判断payload属性
const makeBarFromFoo = (foo: AnyFoo): AnyBar => {
  if (foo.key === 'people' && foo.payload.name && foo.payload.age) {
    return {
      key: foo.key,
      payload: foo.payload,
    }
  }
  //...剩余代码
}

这种方法对于小范围情况还可以,但是对于大量类型处理就会有点力不从心了

  • 终极方案rest解构
    这里我们的本质需求是从一个对象中获取关联的两个属性,且确保关联不丢失。这个需求换种思路,就是从指定对象中去除一些属性,返回剩余的。这个正好就是rest解构的场景。

所以就是如下的代码

const makeBarFromFoo = (foo: AnyFoo): AnyBar => {
  const { info, ...rest } = foo;
  return rest
}

image.png
ts识别也正常,没有报错


参考链接:

相关文章
|
3月前
|
JavaScript
typeScript基础(5)_对象的类型-interfaces接口
本文介绍了TypeScript中接口(interfaces)的基本概念和用法,包括如何定义接口、接口的简单使用、自定义属性、以及如何使用`readonly`关键字定义只读属性。接口在TypeScript中是定义对象形状的重要方式,可以规定对象的必有属性、可选属性、自定义属性和只读属性。
47 1
|
4月前
|
开发框架 缓存 前端开发
基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用
基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用
|
3月前
|
JavaScript 编译器
typescript之泛型
typescript之泛型
135 60
|
2月前
|
JavaScript 前端开发
TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第11天】TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
3月前
|
JavaScript 安全
typeScript进阶(14)_泛型和注意事项
TypeScript中的泛型允许创建可重用的代码。泛型可以定义函数、接口、类,支持传递类型参数,实现类型安全。泛型可以用于数组,约束类型参数必须符合特定的接口,也可以在接口和类中使用。泛型类可以包含多个类型参数,甚至在泛型约束中使用类型参数。
28 1
typeScript进阶(14)_泛型和注意事项
|
2月前
|
JavaScript 安全 前端开发
TypeScript :枚举&字符&泛型
本文介绍了 TypeScript 中的泛型、约束、枚举和字符操作的基本用法。通过示例代码展示了如何定义和使用泛型函数、类和接口,以及如何利用 `keyof` 约束类型。此外,还介绍了枚举的定义和使用,包括常量枚举和外部枚举的区别。最后,简要说明了 `?.` 和 `??` 操作符的用途,帮助处理可能为空的属性和提供默认值。
|
4月前
|
JavaScript 安全 算法
TypeScript:一个好泛型的价值
TypeScript:一个好泛型的价值
|
4月前
|
JavaScript 编译器
typescript 解决变量多类型访问属性报错--工作随记
typescript 解决变量多类型访问属性报错--工作随记
|
4月前
|
开发框架 前端开发 JavaScript
在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口
在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口
|
4月前
|
JavaScript 前端开发 API
Vue 3+TypeScript项目实战:解锁vue-next-admin中的全局挂载对象接口,让跨组件共享变得高效而优雅!
【8月更文挑战第3天】在构建Vue 3与TypeScript及vue-next-admin框架的应用时,为提高多组件间共享数据或方法的效率和可维护性,全局挂载对象接口成为关键。本文通过问答形式介绍其必要性和实现方法:首先定义全局接口及其实现,如日期格式化工具;接着在`main.ts`中通过`app.config.globalProperties`将其挂载;最后在组件内通过Composition API的`getCurrentInstance`访问。这种方式简化了跨组件通信,增强了代码复用性和维护性。
66 0