提问者:小点点

Typescript-使用泛型扩展具有union类型属性的接口


我有这样的接口

interface Test {
  values: string[] | number[] | Date[]
  // more properties
}

我想创建另一个使类型特定的接口。我尝试的是:

interface TestWithType<T extends string | number | Date> extends Test {
  values: T[];
}

但是这失败了,错误说Type'string'不能分配给type'Date'

正确的语法是什么?如果属性不是数组,则可以正常工作

interface Test {
  values: string | number | Date
}

interface TestWithType<T extends string | number | Date> extends Test {
  values: T;
}

共2个答案

匿名用户

正如您所注意到的,通过应用创建类型为string | number | Date的元素数组,您不会得到string[]| number[]| Date[]。由于(string | number | Date)[string[]| number[]| Date[]更宽,因此会出现编译器错误,因为无法加宽子类型的属性。

@MattMcCutchen的回答给出了一些解决方法。还有一种方法:

如果您有一个类似于string | number | Date的并集,并且希望通过编程将其转换为类似于string[]| number[]| Date[]的数组并集,则可以使用分布式条件类型:

type DistributeArray<T> = T extends any ? T[] : never;

然后,您可以根据DistributerRay定义TestWithType

// no error:
interface TestWithType<T extends string | number | Date> extends Test {
  values: DistributeArray<T>;
}

并验证其行为是否符合预期:

declare const testWithString: TestWithType<string>
testWithString.values; // string[]

declare const testWithDate: TestWithType<Date>
testWithDate.values; // Date[]

declare const testWithStringOrNumber: TestWithType<string | number>
testWithStringOrNumber.values; // string[] | number[]

希望有帮助。祝你好运

编辑:

作为一个相关的问题,有没有办法禁止将联合类型传递给泛型?(如要求最多只指定字符串、数字或日期中的一个)

是的,这是可能的,但它需要滥用类型系统,使我感到不舒服。如果你不需要的话,我建议你不要那样做。这是:

type DistributeArray<T> = T extends any ? T[] : never;
type NotAUnion<T> = [T] extends [infer U] ? U extends any ? 
  T extends U ? unknown : never : never : never
type ErrorMsg = "NO UNIONS ALLOWED, PAL"
interface TestWithType<T extends (
  unknown extends NotAUnion<T> ? string | number | Date : ErrorMsg
)> extends Test {
  values: DistributeArray<T>
}

declare const testWithString: TestWithType<string> // okay

declare const testWithDate: TestWithType<Date> // okay

declare const testWithStringOrNumber: TestWithType<string | number> // error:
// 'string | number' does not satisfy the constraint '"NO UNIONS ALLOWED, PAL"'.

类型nota并集

祝你好运!

匿名用户

两种可能的解决方案,使用查找类型或条件类型:

enum TestType { STRING, NUMBER, DATE }
interface TestTypeMapping {
  [TestType.STRING]: string[];
  [TestType.NUMBER]: number[];
  [TestType.DATE]: Date[];
}
interface TestWithType1<T extends TestType> extends Test {
  values: TestTypeMapping[T];
}

type TypeToArrayType<T> =
  T extends string ? string[] :
  T extends number ? number[] :
  T extends Date ? Date[] :
  never[];
interface TestWithType2<T extends string | number | Date> extends Test {
  values: TypeToArrayType<T>;
}