【问题标题】:TypeScript: How to write a function with conditional return typeTypeScript:如何编写具有条件返回类型的函数
【发布时间】:2018-11-11 12:18:30
【问题描述】:
export type Name = { name: string }
export type Id = { id: number }
export type Value<T> = T extends string ? Name : Id

export function create<T extends string | number>(value: T): Value<T> {
    if (typeof value === "string") return { name: value }

    return { id: value }
}

我正在玩 TypeScript 中的条件类型。我想写一个有条件返回类型的函数。如果函数得到一个字符串,则返回一个名称,否则返回一个 ID。

我的退货语句出现以下错误:

Type '{ name: T & string; }' is not assignable to type 'Value<T>'.

我错过了什么?谢谢!

编辑:直接取自 Anders Hejlsberg 在 Build 2018 上的演讲: https://youtu.be/hDACN-BGvI8?t=2241

他甚至说“我们不必再编写函数重载了……”

如果我将代码更改为声明,编译错误就会消失:

export type Name = { name: string }
export type Id = { id: number }

export type Value<T> = T extends string ? Name : Id

declare function create<T extends string | number>(value: T): Value<T>

const a = create("Bob")     // a : Name
const b = create(5)         // b : Id

所以我们可以声明函数签名。我想我的问题就变成了,我们将如何实际实现该功能?

【问题讨论】:

  • 重载函数或“强制转换”返回值是否仍然是最先进的技术?

标签: typescript


【解决方案1】:

这是另一个效果很好的解决方案。

我比公认的答案更喜欢这个解决方案因为你会在编码时得到正确的类型推导

悬停时可以看到,它正确推断了类型。

这是截图中的代码:

export type Name = { name: string }
export type Id = { id: number }
export type Value<T extends string | number> = T extends string ? Name : Id

export function create<T extends string | number>(value: T): Value<T> {
    if (typeof value === 'string') return { name: value } as unknown as Value<T>

    return { id: value } as unknown as Value<T>
}

【讨论】:

  • 这是一个很好的解决方案,尽管不需要将类型转换为 unknown 然后 Value&lt;T&gt; ;你可以直接输入 cast to Value&lt;T&gt;
【解决方案2】:

我不确定我是否完全理解您的示例,但我认为这就是您想要的?

type Name = { name: string }
type Id = { id: number }
type Value = Name | Id

export function create(value: string | number): Value {
  if (typeof value === "string") return { name: value }
  return { id: value }
}

看,我已经从您的示例中删除了所有泛型,因为在这种情况下我认为不需要它们

【讨论】:

  • 问题是你的版本create(0)返回Name | Id而不是预期的Id
  • 如果有几个重载就可以了。但是,我的理解是条件类型可以代替重载......
【解决方案3】:

问题在于函数T 内部未知,因此您无法真正为Value&lt;T&gt; 赋值。一种选择是使用类型断言。一个更安全的选择是使用单独的实现签名,这样可以更轻松地处理输入和输出类型:

export function create<T extends string | number>(value: T): Value<T> // public signature
export function create(value: string | number): Name | Id { // more relaxed private implementation signature 
    if (typeof value === "string") return { name: value }

    return { id: value }
}

【讨论】:

  • 有趣的解决方案。与仅使用两个普通重载相比,我只是没有看到我们有什么收获?
  • 你少了一个超载……肯定没有太大的优势,如果你添加更多它可能是值得的..
  • @titan cernicova-dragomir 我很困惑为什么 T 在函数内部是未知的,即使我们消除了可能性。我很难理解这一点。如何确保公众将收到第一个签名 BTW?
  • 哇,真不敢相信这有效...
猜你喜欢
  • 2020-05-09
  • 2021-01-03
  • 2019-04-09
  • 2021-05-03
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多