【问题标题】:Typescript - inherit return type from the argument property when argument is unionTypescript - 当参数是联合时,从参数属性继承返回类型
【发布时间】:2021-06-02 12:14:11
【问题描述】:

我有一个类型,它是通用接口与 2 个通用类型的联合。其中一个是枚举,另一个对于每个枚举值都不同。

调用函数时,TS 根据函数调用中的枚举值正确识别数据类型,但不明白返回值应该与该对象的属性之一相同

代码:

interface Base<D = any, T = string> {
  data: D;
  type: T;
}

enum BaseTypes {
  foo,
  bar,
  boo,
}

type BaseSet = Base<number, BaseTypes.foo> | Base<string, BaseTypes.bar> | Base<{d: number}, BaseType.boo>;

function processData(payload: BaseSet): typeof payload.data {
  return payload.data;
}

// a type should be number
// but it's number | string
const a = processData({ data: 1, type: BaseTypes.foo });

我的理解是a的类型应该继承自payload数据类型,在这种情况下应该是string。我的两个问题是:

  • 为什么它不起作用?
  • 我怎样才能让它工作?

【问题讨论】:

  • payload 不是泛型的返回类型是Base&lt;D = any, T = string&gt;.data: string | number。我们可以通过使函数泛型来解决这个问题。我会写一个答案。

标签: typescript types typescript-generics


【解决方案1】:

问题:函数不是通用的

processData 函数不是通用函数。它接受一个对象参数,该参数是两种BaseSet 类型之一。

它的返回输入为typeof payload.data。关键区别是:payload 的类型是什么? 在这个版本中,payload 的类型是联合类型 BaseSet,因此返回类型 typeof payload.data 是联合类型所有data 类型都输入BaseSet

这是因为我们在执行processData(payload: BaseSet) 时将payload 的类型设置为BaseSet。如果我们有更多关于 payload 变量类型的具体信息,那么这些信息就会丢失。为了为payload 保留更具体的类型,我们需要使用泛型。

解决方案:泛型函数

我们可以使processData 成为一个泛型函数,并说返回的类型取决于调用它的参数的类型。这里我使用通用的D 来指代data 属性的类型。

function processData<D>(payload: BaseSet & {data: D}): D {
  return payload.data;
}

我们说payload 必须具有{data: D} 类型并且也是有效的BaseSet。这会强制执行配对,但也允许 typescript 为 data 推断出更具体的版本类型。然后我们说这个特定类型将是我们的返回类型。

测试一下,我们得到了正确的返回类型,如果datatype 不匹配也会出错。

const a = processData({ data: 1, type: BaseTypes.foo }); // number
const b = processData({ data: "hahaha", type: BaseTypes.foo }); // error
const c = processData({ data: "hahaha", type: BaseTypes.bar }); // string

Typescript Playground Link

有多种方法可以定义泛型函数。泛型是data 类型还是整个payload 类型?我们应该对D 应用任何约束还是&amp; BaseSet 足够?有趣的是,这个版本给了我文字字符串和数字作为返回类型。

function processData<D extends BaseSet['data']>(payload: BaseSet & {data: D}): D {
  return payload.data;
}
const a = processData({ data: 1, type: BaseTypes.foo }); // 1
const b = processData({ data: "hahaha", type: BaseTypes.foo }); // error
const c = processData({ data: "hahaha", type: BaseTypes.bar }); // "hahaha"

Typescript Playground Link

【讨论】:

  • 感谢您详尽的回答@linda-paiste!赞赏!但是,如果我们使用 object 作为数据值 typescript type BaseSet = Base | 它会破坏验证。 Base; processData({ data: {text: 'test', foo: 2}, type: BaseTypes.foo }); // 这应该会报错Typescript Playground Link
猜你喜欢
  • 2015-11-18
  • 2020-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多