TypeScript'te iç içe geçmiş bir nesnenin anahtarlarını nasıl tip ipucu verilir?
Tümü bir temel arabirimi genişleten bazı arabirimlerim var. Örneğin:
interface Base {
sharedKey : "a" | "b"
}
interface ChildOne extends Base {
sharedKey : "a",
keyOne : "child_one"
}
interface ChildTwo extends Base {
sharedKey : "b",
keyTwo : "child_two"
}
Tüm genişletilmiş arayüzler, tanımı daraltan paylaşılan bir anahtar tanımlar. Birçok IDE'de gördüğüm, iç içe geçmiş nesnelerin anahtarlarını tip ipucu olarak gösteren bir özelliği uygulamaya çalışıyorum. Şöyle bir fonksiyonu düşünün:
function doStuff( sharedKey, privateKey ) { // Something here};
Bu fonksiyon, a veya b olan bir anahtarı kabul edecek ve bu anahtara bağlı olarak privateKey'in keyOne veya keyTwo olmasına izin verilip verilmediğini tespit edecek.
Örneğin bu izin verilmeli:
doStuff( 'a', 'keyOne' ); // Allowed
Ama bu yapmamalı:
doStuff( 'b', 'keyOne' ); // Error: keyOne does not exist on ChildTwo
Bu mümkün mü? Şablon dizelerini kullanmayı denedim, ancak sorun şu ki şablon dizelerinden dönen tür bir string idi ve bunu bir Tür olarak kullanamıyorum.
Record<Unions['sharedKey'], Unions> kullanmak (burada Unions çocuk arayüzlerin bir birleşimi) bazı tür ipuçları sağlar, bu da hiç var olmayan anahtarların kullanılmasını önlemeye yardımcı olur, ancak yine de diğer arayüzlerden anahtarların kullanılmasına izin verir.
Cevap #1
Olası bir çözüm, fonksiyon aşırı yüklemelerini kullanmak olabilir:
interface ChildOne {
sharedKey : "a",
keyOne : "child_one"
}
interface ChildTwo {
sharedKey : "b",
keyTwo : "child_two"
}
function doStuff( sharedKey: ChildOne['sharedKey'], privateKey: keyof ChildOne ):number
function doStuff( sharedKey: ChildTwo['sharedKey'], privateKey: keyof ChildTwo ):number
function doStuff( sharedKey: string, privateKey: string) {
// Something here
return 0;
};
doStuff( 'a', 'keyOne' ); // Allowed
doStuff( 'b', 'keyOne' ); // Error: keyOne does not exist on ChildTwo
Cevap #2
Bu, Extract ile genişletilebilir bir şekilde yapılabilir:
function doStuff<
SharedKey extends Union["sharedKey"],
PrivateKey extends Exclude<keyof Extract<Union, { sharedKey: SharedKey }>, "sharedKey">
>(sharedKey: SharedKey, privateKey: PrivateKey) { ... }
burada Union, tüm çocukların birleşimidir.
Kullanılan ortak anahtarı alırsınız, sonra o anahtara sahip olan birliği çocuktan çıkarmak için anahtarı kullanırsınız.
Doğru çocuğa sahip olduğunuzda, o çocuğun anahtarlarını "sharedKey" hariç alırsınız (yoksa type hinting içinde sharedKey'in de görünmesini istiyorsanız?).
Özünde, bu temel olarak @PabloCarrilloAlvarez'in cevabını generikler olarak ifade eden aynı fikir.
doStuff("a", "keyOne") // good
doStuff("b", "keyOne") // error
doStuff("b", "keyTwo") // good
doStuff("a", "keyTwo") // error
Aşağıdaki birkaç örnek çağrıyla bu yöntemi deneyebilirsiniz:
Oyun alanı
