javascript - Güçlü tipli bir nesnede TypeScript döngüsel kısıtlama


Bir nesneyi şöyle tanımladım:

const example = {
  morning: (name) => `Good morning ${name}!`,
  evening: (name) => `Good evening ${name}!`
}

Ham hâlinde, nesnenin güçlü yazılmış anahtarları vardır (onlara nokta gösterimi ile erişebilirim), ama değerler her şey olabilir. Bu yüzden bir tür imzası eklemeye karar verdim:

const example: {
  [K: string]: (name: string) => string;
} = {
  morning: (name) => `Good morning ${name}!`,
  evening: (name) => `Good evening ${name}!`
}

Şimdi nesne değerlerim güçlü bir şekilde tiplendirilmiş durumda, ama anahtarlar öyle değil ve TypeScript şikayet etmeksizin nokta notasyonu kullanarak herhangi bir anahtara erişmeye çalışabilirim (örneğin, console.log(example.doesntexist) yazabilirim ve nesne anahtarları için editör otomatik tamamlama da alamam). Bu yüzden bunu denedim:

const example: {
  [K in keyof typeof example]: (name: string) => string;
} = {
  morning: (name) => `Good morning ${name}!`,
  evening: (name) => `Good evening ${name}!`
}

Şimdi bir döngüsel kısıtlama hatası alıyorum (Tip parametresi 'K' döngüsel bir kısıtlamaya sahip ve 'example' kendi tür açıklamasında doğrudan veya dolaylı olarak referans ediliyor). Bunu şöyle bir şey yapabileceğimin farkındayım

const example: {
  [K in 'morning' | 'evening']: (name: string) => string;
} = {
  morning: (name) => `Good morning ${name}!`,
  evening: (name) => `Good evening ${name}!`
}

ama bu gereksiz (ve ben gereksizliğinden/kod çoğaltılmasından nefret ederim). İstediğimi yapmanın bir yolu var mı?

Cevap #1

Bunu en az biraz ek kod olmadan yapmanın bir yolu yok.

Böyle durumlarda genellikle bir kısıtlı genel kimlik fonksiyonu yardımcı fonksiyonu yazarım. Bu, sizin yazmaya çalıştığınız döngüsel açıklama gibi davranır:

const asExample = <K extends PropertyKey>(
  x: { [P in K]: (name: string) => string }
) => x;

Bu fonksiyon, sadece türü bazı anahtar benzeri K için anahtarları K ve değerleri (name: string) => string olan bir girişi kabul edecektir ve girdiyi döndürür. Fonksiyon K üzerinde generik olduğundan, derleyici K’yı takip etmeye devam edecek ve bunu string gibi daha geniş bir türe dönüştürmeyecektir.

Haydi onu test edelim:

const example = asExample({
    morning: name => `Good morning ${name}!`,
    evening: name => `Good evening ${name}!`
})

example.morning("Alice") // okay
example.day // error! 
// ---> ~~~
// Property 'day' does not exist on type
// '{ morning: (name: string) => string; evening: (name: string) => string; }'

example'in morning ve evening anahtarlarında özellikleri olduğu bilinir, ancak day anahtarında yoktur, bunu görebilirsiniz.

Ve kötü bir girdi üzerinde asExample() çağırmayı denerseniz, istediğiniz hataları alırsınız:

const badExample = asExample({
    afternoon: name => `Good afternoon ${name}!`.length, // error!
    // --------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // number is not assignable to string
    night: "Good night everyone!" // error!
//  ~~~~~ <-- string is not assignable to (name: string) => string
})

Koda yönelik oyun alanı bağlantısı