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!
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