TypeScript'te lambda fonksiyonu tanımlama problemi


Ben sadece TypeScript'te aşağıdaki lambda fonksiyon tanımıyla takıldım. Bu belki de yeni başlayan bir sorudur.

type P = { id: string }
type F = <T>(x: T) => string
const f: F = (x: P) => x.id
f({ id: 'abc' }

Ama aşağıdaki şekilde şikayet etmeye devam etti:

‘(x: P) => string’ türü, ‘F’ türüne atanamaz.

Parametre türleri 'x' ve 'x' uyumsuz.

‘T’ tipi ‘P’ tipine atanamaz.

Güncelleme #1

Sadece neden genel bir fonksiyona ihtiyacım olduğunu açıklıyorum. Bir girdi nesnesini doğrulamak için bir fonksiyon yapıyorum ve nesnenin her yolu doğrulama için aşağıdaki gibi yapılandırılabilir:

type Option = { path: string; isVerified: <T>(t: T) => boolean }

const verify = (obj: any, options: Option[]): boolean => {
  const results = options.map(option => {
    const { path, isVerified } = option
    return isVerified(obj[path])
  })
  return results.every(x => x)
}

type A = number
type B = string
const obj = { a: 1 as A, b: 'abc' as B }

verify(obj, [
  { path: 'a', isVerified: (x: A): boolean => x > 0 },
  { path: 'b', isVerified: (x: B): boolean => x.startsWith('a') }
])

Güncelleme #2

İlk cevap için teşekkürler, Güncelleme #1'de listelenen problemi çözdü. Ancak, Güncelleme #1 aslında basitleştirilmiş bir problem. Gerçek durumda, Option tipi altındaki path ya string ya da RegExg olabilir, bu da aşağıdaki gibi Paths[K] ile verilen cevaptaki kodu geçersiz kılıyor:

type Option = { path: string | RegExp; isVerified: <T>(t: T) => boolean }

const verify = (obj: any, options: Option[]): boolean => {
  const results = Object.keys(obj).map(k => {
    const verifiers = options
      .filter(opt =>
        typeof opt.path === 'string' ? opt.path === k : opt.path.test(k)
      )
      .map(x => x.isVerified)
    return verifiers.every(verify => verify(obj[k]))
  })

  return results.every(x => x)
}

type A = number
type B = string
const obj = { a: 1 as A, b: 'abc' as B }

verify(obj, [
  { path: 'a', isVerified: (x: A): boolean => x > 0 },
  { path: /b/, isVerified: (x: B): boolean => x.startsWith('a') }
])

Daha fazla bilgi için Play ground bağlantısına bakın.

Ve aşağıda sürekli şikayet ediyordu:

‘(x: number) => boolean’ türü, ‘(t: T) => boolean’ türüne atanamaz.

‘x’ ve ‘t’ parametre türleri uyumsuz.

‘T’ tipi ‘number’ tipine atanamaz.

Cevap #1

Örnek kod ile ilgili olarak:

type P = { id: string }
type F = <T>(x: T) => string
const f: F = (x: P) => x.id

F türü, herhangi bir T türünden string türüne genel bir işlevdir; yani esasen, bir işlevin F türünde olabilmesi için herhangi bir türde argümanı kabul etmesi gerekir. İşleviniz f yalnızca P türündeki argümanları kabul ettiğinden, bu işlev F türünde değildir.

Çözmeye çalıştığınız gerçek sorunla ilgili olarak, burada asıl mesele Option'ın bir birlik türü olması gerektiğidir, çünkü isVerified için parametre türü path değerine bağlıdır:

type Option = {
    path: 'a',
    isVerified: (t: number) => boolean
} | {
    path: 'b',
    isVerified: (t: string) => boolean
}

Bu birlik tipini gerçek bir kullanım durumu için (daha fazla alan olabileceği yerlerde) daha kolay tanımlamak için, doğruladığınız nesneler için bir Paths tipi tanımlayın ve ardından Option tipini bir eşlenmiş tip olarak oluşturun:

type Paths = { a: number, b: string }

type Option = {
  [K in keyof Paths]: {
    path: K,
    isVerified: (t: Paths[K]) => boolean
  }
}[keyof Paths]

Artık verify fonksiyon çağrınız tip kontrolünden geçecek ve sizin açıkça anotasyon yazmanıza gerek kalmadan parametre tiplerini bile çıkaracaktır:

const obj = { a: 1, b: 'abc' }

// OK
verify(obj, [
  { path: 'a', isVerified: x => x > 0 },
  { path: 'b', isVerified: x => x.startsWith('a') }
])

Ayrıca, isVerified yanlış parametre türüne sahipse (istendiği gibi) bir tür hatası alırsınız, çünkü path özelliği isVerified'in hangi türe sahip olması gerektiğini daraltır:

// type error
verify(obj, [
  { path: 'a', isVerified: x => x.startsWith('a') },
  { path: 'b', isVerified: x => x > 0 }
])

Yapmanız gereken bir düzenleme daha var - verify fonksiyonunda bir tür doğrulaması, çünkü aksi takdirde isVerified parametresinin türü never olarak çıkarılır (yani kesişim number & string):

const verify = (obj: any, options: Option[]): boolean => {
  const results = options.map(option => {
    const { path, isVerified } = option

    // this line needs a type assertion
    return (isVerified as (t: Paths[typeof path]) => boolean)(obj[path])
  })
  return results.every(x => x)
}

Oyun Alanı Bağlantısı