类型保护用于具有相同字段的类型

huangapple go评论65阅读模式
英文:

Type guards for types with the same fields

问题

我遇到了结构类型的一个限制,想知道是否有绕过它的方法。

假设我有以下类型的层次结构:

type Message = HelloMessage | GoodbyeMessage

type HelloMessage = {
  message: string
}

type GoodbyeMessage = {
  message: string
}

并且我们定义了一些类型守卫如下:

function isHello(value: any): value is HelloMessage {
    return !!value && !!value.message && typeof value.message === "string" && value.message.includes("hello")
}

function isGoodbye(value: any): value is GoodbyeMessage {
    return !!value && !!value.message && typeof value.message === "string" && value.message includes("goodbye")
}

尝试在函数中使用这些类型守卫会导致编译器将后续使用的部分类型为 never

function someFunc(input: Message): string {
    if (isHello(input)) {
        return input.message
    }
    if (isGoodbye(input)) {
        return input.message // 编译器在这里报错
    }
    return "unknown"
}

一些明显的解决方案是将输入类型标记为 any 或在 if 语句内将 input 强制转换为 GoodbyeMessage,但这两种方法都不太优雅。
这是否只是 TypeScript 结构类型的限制,或者是否有一些其他方法可以让它按照我的期望工作?

英文:

I stumbled into a limitation of structural typing and wondered if there was a way round it.

Suppose I have a hierarchy of types as follows:

type Message = HelloMessage | GoodbyeMessage

type HelloMessage = {
  message: string
}

type GoodbyeMessage = {
  message: string
}

and we define some type guards as follows:

function isHello(value: any): value is HelloMessage {
    return !!value && !!value.message && typeof value.message === "string" && value.message.includes("hello")
}

function isGoodbye(value: any): value is GoodbyeMessage {
    return !!value && !!value.message && typeof value.message === "string" && value.message.includes("goodbye")
}

Attempting to use the type guards in a function leads to subsequent uses being typed as never by the compiler:

function someFunc(input: Message): string {
    if (isHello(input)) {
        return input.message
    }
    if (isGoodbye(input)) {
        return input.message // compiler errors here
    }
    return "unknown"
}

Some obvious solutions are to type the input as any or cast input as GoodbyeMessage inside the if statement, but neither of these feel particularly elegant.
Is this simply a limitation of Typescript's structural nature of type or is there some other magic that can be incanted to make it work as I'm expecting?

答案1

得分: 1

你说得对,这是类型系统的一个限制。即使类型守卫起作用,你也无法真正确定一个函数是否传递了 HelloMessage 还是 GoodbyeMessage,因为它只看到 { message: string }。你可以改用标记联合类型,这种类型可以使用类型守卫进行区分:

type Message = HelloMessage | GoodbyeMessage;

type HelloMessage = {
  type: "hello";
  message: string;
}

type GoodbyeMessage = {
  type: "goodbye";
  message: string;
}

// 更倾向于使用 `unknown` 来提高类型安全性。
function isHello(value: unknown): value is HelloMessage {
  return typeof value === "object"
      && value !== null
      && "type" in value
      && typeof value.type === "string"
      && value.type === "hello";
}

function isGoodbye(value: unknown): value is GoodbyeMessage {
  return typeof value === "object"
      && value !== null
      && "type" in value
      && typeof value.type === "string"
      && value.type === "goodbye";
}
英文:

You're right, this is a limitation of the typing system. Even if the type guards worked, you couldn't actually tell if a function was passed HelloMessage or GoodbyeMessage, since all it sees is { message: string }. You could instead use a tagged union type, which can be discriminated with type guards:

type Message = HelloMessage | GoodbyeMessage;

type HelloMessage = {
  type: "hello";
  message: string;
}

type GoodbyeMessage = {
  type: "goodbye";
  message: string;
}

// Prefer to use `unknown` for additional type safety.
function isHello(value: unknown): value is HelloMessage {
  return typeof value === "object"
      && value !== null
      && "type" in value
      && typeof value.type === "string"
      && value.type === "hello";
}

function isGoodbye(value: unknown): value is GoodbyeMessage {
  return typeof value === "object"
      && value !== null
      && "type" in value
      && typeof value.type === "string"
      && value.type === "goodbye";
}

huangapple
  • 本文由 发表于 2023年3月8日 17:53:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75671548.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定