簡體   English   中英

獲取特定類型屬性的所有虛線 object 路徑的並集

[英]Get union of all dotted object paths to properties of a particular type

我有一個 object 代表 dom 樹可見性

const visibilities = {
    food: {
        visible: true,
        fruit: {
            visible: true,
            apple: {
                visible: false
            }
        },
        snack: {visible: false}
    }
}

我想通過使用 util function 獲得蘋果的可見性

getVisibilities(visibilities, 'food.fruit.apple')

但我不知道如何輸入第二個參數。 當 visibilities[K] 是 {visibility: boolean} 類型時,我嘗試連接鍵

type VisibilityString<Prop extends {[key: string]: any}> = {[K in keyof Prop]: Prop[K] 
   extends {visible: boolean} ? K 
     extends string ? K | `${K}.${VisibilityArray<Prop[K]>}`: never : never}[keyof Prop]

得到 TypeError: VisibilityArray<Prop[K]> 不是字符串

將第二個參數更改為數組有效,但 typescript 在版本 4.4.4 中仍然給出錯誤

游樂場鏈接

這些深度嵌套的遞歸條件類型通常有非常令人驚訝和不愉快的邊緣情況,並且在滿足您所有需求的類型與導致令人討厭的循環警告和編譯器速度變慢的類型之間可能只有一線之隔。 因此,雖然我在下面提出了一種適用於您的示例代碼的可能方法,但請注意,這很棘手,您很可能會遇到需要完全重構才能克服的問題。


無論如何,這是一種方法:

type _DKM<T, V> =
  (T extends V ? "" : never) |
  (T extends object ? { [K in Exclude<keyof T, symbol>]:
    `${K}.${_DKM<T[K], V>}` }[Exclude<keyof T, symbol>] : never)

type TrimTrailingDot<T extends string> = T extends `${infer R}.` ? R : T;

type DeepKeysMatching<T, V> = TrimTrailingDot<_DKM<T, V>>

輔助類型_DKM<T, V>代表DeepKeysMatching<T, V>並完成大部分工作。 這個想法是它采用類型T並且應該生成該類型中所有指向類型V值的路徑的聯合。 我們會看到它實際上生成了這些路徑的並集,並在它們后面附加了一個尾隨點,因此我們需要在之后修剪這個點。

基本上:如果T本身是V類型,那么我們至少要返回空白路徑"" 否則我們不返回never 如果T不是 object 類型,我們就完成了; 否則我們 map _DKM<T[K], V>遍歷鍵K處的每個屬性,並在每個鍵K和一個點之前加上它。 這給了我們想要的一切,除了尾隨的點。

因此TrimTrailingDot<T>將從字符串中刪除尾隨點(如果有的話)。

最后DeepKeysMatching<T, V>被定義為TrimTrailingDot<_DKM<T, V>> ,所以我們只是去掉那個點。


有了它,我們可以定義getVisibilities()

declare function getVisibilities<T>(visibilities: T,
  path: DeepKeysMatching<T, { visible: boolean }> & {} 
): boolean;

它在visibilities參數的類型T中是通用的,然后我們將path參數限制為指向{visible: boolean}類型屬性的T路徑的並集,因此DeepKeysMatching<T, { visible: boolean }>

順便說一下, & {}並沒有真正對類型做任何事情(空的 object 類型{}匹配除undefinednull之外的所有內容,因此將一個string與它相交最終將成為相同的字符串),但它確實給出了IntelliSense 提示我們希望它將path類型顯示為字符串文字的聯合,而不是類型別名DeepKeysMatching<{...}, { visible: boolean }> 別名在某些情況下可能很有用,但您可能希望向調用者顯示特定的值列表。


讓我們測試一下:

const visibilities = {
  food: {
    visible: true,
    fruit: {
      visible: true,
      apple: {
        visible: false
      }
    },
    snack: { visible: false }
  }
}

getVisibilities(visibilities, "food.fruit.apple");
// function getVisibilities(
//   visibilities: {...}, 
//   path: "food.fruit.apple" | "food" | "food.fruit" | "food.snack"
// ): boolean

看起來挺好的。 當我們調用getVisibilities(visibilities, ...時,系統會提示我們輸入類型為"food.fruit.apple" | "food" | "food.fruit" | "food.snack"path參數。

getVisibilities(
   { a: 1, b: { c: { d: { visible: false } } } }, 
   "b.c.d"
);

同樣看起來不錯,提示我們只能接受路徑"b.c.d"


所以我們完成了,它起作用了。 正如我之前所說,我確信有很多邊緣情況需要處理,但這至少回答了所提出的問題。

游樂場代碼鏈接

暫無
暫無

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

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