[英]Typescript - Using generics to extend an interface with a property of union type
我有這樣的界面
interface Test {
values: string[] | number[] | Date[]
// more properties
}
我想創建另一個使類型特定的接口。 我正在嘗試的是:
interface TestWithType<T extends string | number | Date> extends Test {
values: T[];
}
但這失敗並顯示一條錯誤消息,提示無法將Type 'string' is not assignable to type 'Date'
。
正確的語法是什么? 如果屬性values
不是數組,則可以正常工作
interface Test {
values: string | number | Date
}
interface TestWithType<T extends string | number | Date> extends Test {
values: T;
}
使用查找類型或條件類型的兩種可能的解決方案:
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>;
}
如您所知,您不會得到string[] | number[] | Date[]
string[] | number[] | Date[]
string[] | number[] | Date[]
通過應用使string | number | Date
類型的元素數組string | number | Date
string | number | Date
string | number | Date
。 並且由於(string | number | Date)[]
比string[] | number[] | Date[]
string[] | number[] | Date[]
string[] | number[] | Date[]
,由於無法擴展子類型的屬性,因此會出現編譯器錯誤。
@MattMcCutchen的答案提供了一些解決方法。 這是另一種方式:
如果您有類似string | number | Date
的聯合string | number | Date
string | number | Date
string | number | Date
並希望以編程方式將其轉換為諸如string[] | number[] | Date[]
的數組的並集 string[] | number[] | Date[]
string[] | number[] | Date[]
,可以使用分布式條件類型 :
type DistributeArray<T> = T extends any ? T[] : never;
然后,您可以根據DistributeArray
定義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"'.
如果T
不是一個並集,則類型NotAUnion<T>
計算結果為unknown
(頂部類型)...否則,其計算結果為never
(底部類型)。 然后,在TestWithType
將類型T
約束為unknown extends NotAUnion<T> ? string | number | Date : ErrorMsg
unknown extends NotAUnion<T> ? string | number | Date : ErrorMsg
unknown extends NotAUnion<T> ? string | number | Date : ErrorMsg
,幾乎沒有對循環引用使用規則。 如果將非工會傳遞為T
,則它變為T extends string | number | Date
T extends string | number | Date
T extends string | number | Date
和以前一樣。 如果傳遞並集,則它將成為T extends ErrorMsg
,它是字符串文字類型。 由於聯合永遠不會擴展字符串文字,因此它將失敗...並且您收到的錯誤消息將涉及ErrorMsg
,因此對開發人員ErrorMsg
,應該足以意識到正在發生的事情。 它可以工作,但是非常粗略。 但是,您要了。
再次祝你好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.