首页 > 解决方案 > In typescript, is there a way to ensure that a function's return type is exhaustive?

问题描述

If you have a const enum like

enum Color {
  RED,
  GREEN,
  BLUE,
}

You can write a helper and a switch statement,

function assertNever(x: never): never {
    throw new Error(`Unexpected object: ${x}`)
}

function toString (key: Color): string {
  switch (key) {
    case Color.RED: return 'Red'
    case Color.GREEN: return 'Green'
    case Color.BLUE: return 'Blue'
    default: return assertNever(key)
  }
}

such that if we ever change Color, we must change our toString implementation.

However, if I go the other way,

function fromString (key: string): Color {
  switch (key) {
    case 'Red': return Color.RED
    case 'Green': return Color.GREEN
    case 'Blue': return Color.BLUE
    default: throw new Error(`${key} is not a Color`)
  }
}

It is possible that my fromString implememtation may get out of date with my Color enum.

Is there a way to ensure that there exists some path that returns each kind of Color? Is there are way to ensure that the range of the function is Color?

标签: typescript

解决方案


没有内置功能会自动为您强制执行此操作。函数的实际返回类型比声明的返回类型更具体不被认为是错误...如果函数被声明为返回 astring但实际上总是返回特定的 string "hello",那很好。做相反的事情只是一个错误,其中函数被声明为返回特定的字符串"hello",但实际上返回了一个 general string

一般来说,你可以做的一件事是让 TypeScript 推断函数的返回类型,然后使用编译时检查来确保它是你认为的那样。例如:

// MutuallyExtends<T, U> only compiles if T extends U and U extends T
type MutuallyExtends<T extends U, U extends V, V=T> = true;

// note how the return type is not annotated
function fromString(key: string) {
  switch (key) {
    case 'Red': return Color.RED
    case 'Green': return Color.GREEN
    case 'Blue': return Color.BLUE
    default: throw new Error(`${key} is not a Color`)
  }
  // the next line will error if not exhaustive:
  type Exhaustive = MutuallyExtends<ReturnType<typeof fromString>, Color>
}

上面的编译,但下面会产生一个错误,因为Color.BLUE缺少:

function fromString(key: string) {
  switch (key) {
    case 'Red': return Color.RED
    case 'Green': return Color.GREEN
    default: throw new Error(`${key} is not a Color`)
  }
  type Exhaustive = MutuallyExtends<ReturnType<typeof fromString>, Color> // error!
//  Color does not satisfy constraint Color.RED | Color.GREEN ---> ~~~~~
}

当然,这是一种解决方法。但也许它会帮助你或其他人。希望有一些用处;祝你好运!


推荐阅读