我有这样的接口
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;
}
正如您所注意到的,通过应用创建类型为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>;
}