class 中的混合文字和数组参数类型与泛型

Mixed literal and array parameter type in class with generics

我有一个class这样的

class Schema<T extends "foo" | string[]> {
  constructor(public value: T) {}
}

为此,构造函数应接受字符串文字“foo”或任意字符串数组。实例化应该 return 一个仅使用文字类型的对象(对于数组也是联合文字):

// What I would like to have
new Schema(["x", "y"]) // => Schema<"x" | "y">
new Schema("foo") // => Schema<"foo">

上面的 class 实现不是这种情况(它 return 在提供数组时是 Schema<string[]> 类型的对象)。

但是如果我这样定义我的class

class Schema<T extends "foo" | string> {
  constructor(public value: T[]) {}
}

我失去了呼叫 new Schema("foo") 的选项。

有什么打字功夫可以解决这个问题吗?

这里有最简单的方法:

class Schema<Value extends string, Values extends string[]> {

  constructor(value: Value | [...Values]) { }
}

// What I would like to have
new Schema(["x", "y"]) // Schema<string, ["x", "y"]>
new Schema("foo") // Schema<"foo", string[]>

Playground

正如您已经注意到的,额外的 stringstring[] 泛型存在问题。

我认为我们可以做得更好。

因为 constructor 不允许我们使用泛型,我们可以创建额外的 method 用于推理目的。

class Schema {
  register<Value extends string>(value: Value): asserts this is { value: Value }
  register<Value extends string[]>(value: [...Value]): asserts this is { value: Value[number] }
  register<Value extends string | string[]>(value: Value): asserts this is { value: Value } {


  }
}

const result: Schema = new Schema();
result.value // expected error
result.register(['x', 'y']);
result.value // x | y

Playground

Here you can find docs about asserts and here 关于函数重载。

请告诉我它是否适合您以及是否需要改进。谢谢