2017-08-30 25 views
5

Belli bir türdeki herhangi bir alt anahtarın anahtarlarını ve anahtarlarını almak istediğiniz, yinelemeli olarak yazılmış bir nesnem var. Örneğin,Anahtar iç içe yerleştirilmiş alt nesneler

. Ben birliği türü almak istiyorum Aşağıda:

Örnek
'/another' | '/parent' | '/child' 

: Eğer birlik tipini almak için typeof RouteList keyof kullanabilirsiniz daktilo versiyonunda

export interface RouteEntry { 
    readonly name: string, 
    readonly nested : RouteList | null 
} 
export interface RouteList { 
    readonly [key : string] : RouteEntry 
} 

export const list : RouteList = { 
    '/parent': { 
     name: 'parentTitle', 
     nested: { 
      '/child': { 
       name: 'child', 
       nested: null, 
      }, 
     }, 
    }, 
    '/another': { 
     name: 'anotherTitle', 
     nested: null 
    }, 
} 

:

'/another' | '/parent' 

orada mı İç içe türler de dahil olmak üzere yöntem

cevap

4

Bu zor bir yöntemdir. TypeScript, her ikisi de bu sendika türünü vermek için kullanmak istediklerim olan mapped conditional types ve genel recursive type definitions eksiktir. Ne istediğinizi bazı anlaşmazlık noktaları vardır:

  • bir RouteEntry ait nested özelliği bazen null olacak ve bir şeyler kırmak için keyof null veya null[keyof null] başlangıç ​​değerlendirmek ifadeleri yazabilirsiniz. Birisinin dikkatli olması gerekiyor. Benim geçici çözümüm, hiçbir zaman boş olmayan bir kukla anahtar eklemeyi ve sonra sonunda kaldırarak içerir.
  • Kullandığınız diğer ad (türetme RouteListNestedKeys<X>) kendi başına tanımlanmalı gibi görünüyor ve "döngüsel başvuru" hatası alırsınız. Bir geçici çözüm, sonlu bir düzeyde iç içe geçmişe kadar uzanan bir şey sağlamaktır (diyelim, 9 seviye derin). Bu, derleyicinin yavaşlatmasına neden olabilir, çünkü değerlendirmeyi ertelemek yerine tüm 9 seviyesini sabırsızlıkla değerlendirebilir.
  • Bu, eşlenen türlerin dahil olduğu çok sayıda tür takma adlarına ihtiyaç duyar ve TypeScript 2.6'ya kadar düzeltilemeyecek şekilde eşleştirilen eşleme türlerine sahip bug vardır. Bir workaround genel varsayılan tür parametrelerini kullanmayı içerir.
  • "Kukla anahtarı en sona kaldır" adımında, düzgün çalışması için en az TypeScript 2.4 gerektiren Diff adlı bir tür işlem gerekir.

Tüm bunların anlamı: Çalıştığım bir çözüm var, ama sizi uyarıyorum, karmaşık ve çılgın. Son bir şey ben kodunda damla önce: Eğer

export const list: RouteList = { // ... 

olduğunu
export const list = { // ... 

için, list değişkeninden tipi notunu kaldırmak değiştirmeniz gerekir. RouteList olarak belirtirseniz, TypeScript'in list tam yapısı hakkında bilgi atarsınız ve anahtar türü olarak string'dan başka bir şey elde edemezsiniz. Ek açıklamayı bırakarak, TypeScript'in türden çıkmasına izin verirsiniz, dolayısıyla yuvalanmış yapının tamamını hatırlar.

Tamam, buraya:

type EmptyRouteList = {[K in 'remove_this_value']: RouteEntry}; 
type ValueOf<T> = T[keyof T]; 
type Diff<T extends string, U extends string> = ({[K in T]: K} & 
    {[K in U]: never} & { [K: string]: never })[T]; 
type N0<X extends RouteList> = keyof X 
type N1<X extends RouteList, Y = {[K in keyof X]: N0<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N2<X extends RouteList, Y = {[K in keyof X]: N1<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N3<X extends RouteList, Y = {[K in keyof X]: N2<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N4<X extends RouteList, Y = {[K in keyof X]: N3<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N5<X extends RouteList, Y = {[K in keyof X]: N4<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N6<X extends RouteList, Y = {[K in keyof X]: N5<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N7<X extends RouteList, Y = {[K in keyof X]: N6<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N8<X extends RouteList, Y = {[K in keyof X]: N7<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N9<X extends RouteList, Y = {[K in keyof X]: N8<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type RouteListNestedKeys<X extends RouteList, Y = Diff<N9<X>,'remove_this_value'>> = Y; 

Şunu deneyelim:

export const list = { 
    '/parent': { 
     name: 'parentTitle', 
     nested: { 
      '/child': { 
       name: 'child', 
       nested: null, 
      }, 
     }, 
    }, 
    '/another': { 
     name: 'anotherTitle', 
     nested: null 
    }, 
} 

type ListNestedKeys = RouteListNestedKeys<typeof list> 

Eğer ListNestedKeys incelemek Eğer "parent" | "another" | "child" olduğunu istediğin gibi, göreceksiniz. Buna değip değmeyeceği size kalmış.

Whew! Umarım yardımcı olur. İyi şanslar!

+0

Küçük bir ayrıntı: "readLine [key: string]:" RouteList "öğesinden RouteEntry dizini imzasını kaldırırsanız, bunların hepsi hala çalışır. Görünüşe göre, bu tür imza, "typeof list" olarak gerçek genel argüman sağladığınızda, tür çıkarımında asla kullanılmaz. Bu, her yerde RouteList'i uzatmak yerine 'extends {}' yazmanıza ve 'RouteList 'arayüzünü tamamen cevaptan çıkarmanıza olanak tanır. – artem