簡體   English   中英

如何使用 Typescript 中的通用索引來處理映射類型?

[英]How to address a mapped type with a generic index in Typescript?

我正在嘗試使用泛型類型從映射類型訪問屬性。 但我在下面的評論中得到了錯誤。 輸入這樣的內容的正確方法是什么?

type ThingType = 'typeA' | 'typeB';

interface Thing<T extends ThingType> {
    type: T,
    name: string
};

type Container<T extends ThingType> = {
    [id: string]: Thing<T>
}

type Store<T extends ThingType = ThingType> = {
    [K in T]?: Container<K>
};

const myStore: Store = {
    'typeA': {
        'one': {type: 'typeA', name: 'one'},
        'two': {type: 'typeA', name: 'two'}
    }
};

// This one does not fail
const typeAContainer: Container<'typeA'> | undefined = myStore['typeA'];

function storeThing<T extends ThingType>(thing: Thing<T>) {
    // Error here:
    const container: Container<T> | undefined = myStore[thing.type];
    // Type 'Store<ThingType>[T]' is not assignable to type 'Container<T> | undefined'.
    //  Type 'Container<"typeA"> | Container<"typeB"> | undefined' is not assignable to type 'Container<T> | undefined'.
    //    Type 'Container<"typeA">' is not assignable to type 'Container<T>'.
    //      Type '"typeA"' is not assignable to type 'T'.
    //        '"typeA"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'ThingType'.
    //          Type 'Store<ThingType>[T]' is not assignable to type 'Container<T>'.
    //            Type 'Container<"typeA"> | Container<"typeB"> | undefined' is not assignable to type 'Container<T>'.
    //              Type 'undefined' is not assignable to type 'Container<T>'.

    // ... code here ...
}

storeThing({type: 'typeA', name: 'three'});

編譯器並沒有真正執行大量更高級別的分析來確認依賴於尚未指定的泛型類型參數的類型的可分配性。 storeThing的實現中,您將Store<ThingType>[T]類型的值分配給Container<T> | undefined類型的變量。 Container<T> | undefined ,其中T尚未指定為具體類型。 不幸的是,編譯器在不指定T的情況下無法將它們視為兼容,即使它們與T的所有可能縮小兼容。

這本質上是 TypeScript 的設計限制。 有關此類類似問題,請參閱microsoft/TypeScript#36737microsoft/TypeScript#36349 在這兩種情況下,編譯器都不能遵循泛型索引訪問和另一個兼容類型之間的高階關系。 有一個現有的開放建議microsoft/TypeScript#33014可以更好地處理此類情況,但尚不清楚是否會在那里實施任何事情。

除非這種情況發生,否則我們必須想出一些前進的方向。


在這里進行的合理方法:一旦你完全確信自己正在做的事情是完全安全的(並且要小心;很容易出錯並認為某事不安全),明智地使用類型斷言是合適的:

const container = myStore[thing.type] as Container<T> | undefined; // okay

這類事情是類型斷言的預期用例:您對類型的了解比編譯器可以驗證的更多的情況。 你只是斷言你正在做的事情是安全的,然后繼續前進。 當然,存在一些危險,即您對編譯器撒了謊……或者您的代碼將來會更改並將先前正確的類型斷言變成謊言。 類型斷言將驗證類型安全的負擔從編譯器轉移到了開發者身上,只要你願意承擔這個責任就可以了。


另一種繼續進行的方法:找到一些自包含的類型操作,即使使用未指定的泛型,編譯器也能遵循您的推理。 這是一門藝術,並不總是可能的。 這里我要做的是這個兩步過程:

const myStoreNarrowed: Store<T> = myStore; // okay
const container: Container<T> | undefined = myStoreNarrowed[thing.type]; // okay

編譯器能夠識別將myStore分配給類型為Store<T>的變量是安全的,該類型比Store<ThingType>窄。 然后,它能夠識別出,當您使用T鍵對Store<T>進行索引時,您會得到可分配給Container<T> | undefined的東西。 Container<T> | undefined

我可能會使用這種方法,因為它仍然為您提供了一些類型安全保證,而這些保證在您使用類型斷言時會丟失。 但是,如果所有其他方法都失敗了,那么總會有一個(經過深思熟慮的)類型斷言。


好的,希望有幫助; 祝你好運!

Playground 代碼鏈接


暫無
暫無

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

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