簡體   English   中英

Typescript-使用泛型擴展具有聯合類型屬性的接口

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM