在打字稿中,有没有办法确保函数的返回类型是详尽的?

时间:2019-01-26 18:42:57

标签: typescript

如果您有类似const的枚举

enum Color {
  RED,
  GREEN,
  BLUE,
}

您可以编写一个帮助程序和一个switch语句,

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)
  }
}

这样,如果我们曾经更改Color,就必须更改我们的toString实现。

但是,如果我走另一条路,

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`)
  }
}

我的fromString实现可能与我的Color枚举不合时宜。

是否可以确保存在某种返回每种Color的路径?有没有办法确保函数的范围是Color

3 个答案:

答案 0 :(得分:2)

没有内置功能可以自动为您执行此操作。如果函数的实际返回类型比声明的返回类型更具体,则认为这不是错误...如果函数被声明为返回string,但实际上总是返回特定的字符串{{1} }, 没关系。相反,这只是一个错误,在该函数中声明该函数返回特定的字符串"hello",但实际上返回的是常规的"hello"

一般而言,您可以做的一件事是让TypeScript推断函数的返回类型,然后使用编译时检查来确保它是您想要的。例如:

string

以上编译,但由于缺少// 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

这当然是一种解决方法。但这可能会帮助您或其他人。希望有一定用处;祝你好运!

答案 1 :(得分:1)

有一种可能的解决方法,可能对您有用,不知道我是否正确理解您要实现的目标。

您可以使用所有可能的颜色字符串定义字符串文字类型。然后,当您更改枚举时,首先需要更改toString函数,这将迫使您向颜色名称类型添加另一个值,因为您将没有新颜色的值。然后,这将破坏fromString函数,因此您需要对其进行更新以使构建起作用。更改后的代码如下所示:

enum Color {
  RED,
  GREEN,
  BLUE
}

type ColorName = 'Red' | 'Green' | 'Blue';

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

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

function assertNeverColor(x: never): never {
  throw new Error(`${x} is not a Color`);
}

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

答案 2 :(得分:1)

假设所有值都不同(否则很难映射),您可以执行从枚举到字符串的类型化映射,然后为其构建反向查找并以类型安全的方式使用它。

这是一个例子:

enum Color {
  RED,
  GREEN,
  BLUE,
}

const ENUM_COLOR_TO_STRING: Record<Color, string> = {
  [Color.RED]: 'Red',
  [Color.GREEN]: 'Green',
  [Color.BLUE]: 'Blue',
}

const COLORS: Color[] = Object.keys(ENUM_COLOR_TO_STRING) as unknown as Color[];

const STRING_COLOR_TO_ENUM: Record<string, Color> = COLORS.reduce<any>((acc, colorEnum) => {
  const colorValue = ENUM_COLOR_TO_STRING[colorEnum]

  acc[colorValue] = colorEnum;

  return acc
}, {})

function fromString(key: string): Color {
  const color: Color | undefined = STRING_COLOR_TO_ENUM[key];

  if (!color) {
    throw new Error(`${key} is not a Color`)
  }

  return color
}

Typescript Playground 上的现场演示