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

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识别也正常,没有报错


参考链接:

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

posted on 2022-12-13 21:50  漫思  阅读(125)  评论(0编辑  收藏  举报

导航