簡體   English   中英

如何區分與 generics 的工會?

[英]How to discriminate unions with generics?

我正在努力根據它們的幾何形狀自動推斷不同類型項目的類型(在顯示一些 GeoJSON 數據的上下文中)。

我使用的是泛型類型,因此我沒有設法設置自定義類型保護,因為它可以讓我區分“個人”項目和“聚合”,但不能區分不同類型的“個人”項目。

基本上,我需要推理水平:

  • 從聚合中區分單個項目
  • 區分每個類別中的不同幾何形狀。

我創建了一個簡化的示例,在我的真實應用程序中,我有 4 種不同類型的項目,它們可能具有不同的可能幾何形狀。

這是一個 TypeScript 操場,代碼如下:

type A = {type: "A", a: string}
type B = {type: "B", b: string}
type C = {type: "C", c: string}
type Geometries = A | B | C

type IndividualFeature<G extends A | B = A | B> = { geometry: G, indivAttribute: string}
type AggregateFeature = { geometry: C, aggAttribute: string}

type DisplayableFeature = IndividualFeature | AggregateFeature


const display = (feature: DisplayableFeature) => {
    switch(feature.geometry.type) {
        case "A":
            console.log("type A", feature.geometry.a, feature.indivAttribute);
            return;
        case "B":
            console.log("type B", feature.geometry.b, feature.indivAttribute)
            return;
        case "C": 
            console.log("type C", feature.geometry.c, feature.aggAttribute)
        default:
        // should not happen
    }
}

const indivFeature: IndividualFeature = { geometry: { type: "A", a: "a"}, indivAttribute: "hello indiv"}
const aggFeature: AggregateFeature = { geometry: { type: "C", c: "c"}, aggAttribute: "hello agg"}

幾何圖形被正確區分,但不是單獨與聚合( feature.indivAttribute / feature.aggAttribute觸發錯誤)。 作為記錄,我嘗試了一個 typeguard:這允許我區分“Indiv”和“Aggregates”,但我失去了幾何的區分。

我應該如何構造我的類型/代碼,以便在此示例中將feature.indivAttribute正確識別為有效屬性?

這確實是一個 typescript 限制,即使沒有泛型。 這里有一個現有的 github 問題: microsoft/TypeScript#18758 還有一個近期活動的 PR: microsoft/TypeScript#38839

目前不可能基於嵌套的可區分聯合來縮小聯合范圍。 判別式必須在同一“級別”上。


作為一種解決方法,您可以編寫一個自定義類型保護,如下所示:

type AllTypes = DisplayableFeature["geometry"]["type"] // "A" | "B" | "C"

type FeatueOfType<T extends AllTypes> = {
    "A": IndividualFeature<A>,
    "B": IndividualFeature<B>,
    "C": AggregateFeature
}[T]

function isFeatueOfType<T extends AllTypes>(
    feature: DisplayableFeature, type: T
): feature is FeatueOfType<T> {
    return feature.geometry.type === type
}

FeatueOfType<T>將幾何類型映射到其特征類型。 例如FeatueOfType<"A">將等於IndividualFeature<A> (這種類型可能直接從DisplayableFeature生成,而不是手動編寫,但這可能會變得復雜。)

然后,當您像isFeatueOfType(feature, "A")調用 typeguard 並且feature.geometry.type === type檢查成功時,我們告訴 typescript feature類型必須是FeatueOfType<"A"> ,即IndividualFeature<A>

(請注意,當類型保護中存在錯誤時,例如在上面編寫feature.geometry.type !== type時,typescript 無法捕捉到。因此始終建議正確測試它們。)

用法:

const display = (feature: DisplayableFeature) => {
    if (isFeatueOfType(feature, "A")) {
        doSometingWithA(feature) // typechecks
        console.log("type A", feature.geometry.a, feature.indivAttribute);
    }
    else if (isFeatueOfType(feature, "B")) {
        console.log("type B", feature.geometry.b, feature.indivAttribute)
    }
    else if (isFeatueOfType(feature, "C")) {
        console.log("type C", feature.geometry.c, feature.aggAttribute)
    }
    else {
        throw Error()
    }
}

function doSometingWithA(a: IndividualFeature<A>) {}

游樂場鏈接

也許只是投射功能?

switch(feature.geometry.type) {
        case "A": console.log("type A", feature.geometry.a, (feature as IndividualFeature).indivAttribute);
            return;
        case "B":
            console.log("type B", feature.geometry.b, (feature as IndividualFeature).indivAttribute)
            return;
        case "C": 
            console.log("type C", feature.geometry.c, (feature as AggregateFeature).aggAttribute)
        default:
        // should not happen
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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