Typescript'te bir fonksiyonun doğru tip ile zincirlenmiş değiştiriciler dizisini kabul etmesini nasıl sağlarsınız
Typescript'te doğru tipleme ile fonksiyon değiştirici zinciri kullanarak bir veriyi değiştirmek istiyorum. Bu mümkün mü?
const addA = (data: {}) => {
return {
...data,
a: "test"
}
}
const addB = (data: {}) => {
return {
...data,
b: "test"
}
}
const updateA = (data: {a:string}) => {
return {
...data,
a: data.a + " test"
}
}
const func = <T extends ((data: Record<string, any>) => Record<string, any>)[]>(modifiers: T): unknown => {
return modifiers.reduce((acc, modifier) => {
return modifier(acc)
}, {})
}
console.log(func([addA])) // Pass, Should pass.
console.log(func([addA, addB])) // Pass, Should pass.
console.log(func([addA, addB, updateA])) // Error, Should pass.
console.log(func([updateA])) // Error, Should error. Unexpected runtime undefined value.
Oyun alanı
Cevap #1
Uyarı: Fonksiyonlarınız her zaman girdiyi çıktıya yayar, ancak tipleriniz bunu aslında yansıtmaz. Bunu göstermek için generikler kullanabilirsiniz, ancak bu aşağıdaki çözümü daha zor hale getirir. Fonksiyondan çıkanın en azından girenle aynı olduğunu varsayan bir kural takip edildiği sürece, aşağıdaki çözüm çalışır.
Bir tip parametresi kullanarak fonksiyonlar dizisini bir demet (tuple) olarak çıkarabilirsiniz. Daha sonra, fonksiyonların aslında hangi tipte olmalarını beklediğinizin demetini oluşturmak için özyinelemeli bir koşullu tip kullanabilirsiniz. Daha sonra tip parametresini bu doğrulama tipi ile kesiştirirsiniz. Eğer her şey yolundaysa, inceleme temel olarak bir noop olur, aksi takdirde kesişimden bir hata alırsınız:
type FunctionArray = Array<(p: any) => any>;
type ValidateChain<T extends FunctionArray , Input = {}, Result extends any[] = []> =
T extends [(data: Input) => infer R, ...infer Tail extends FunctionArray] ? ValidateChain<Tail, Input & R, [...Result, T[0]]>:
T extends [(...p: infer P) => infer R, ...infer Tail extends FunctionArray] ? ValidateChain<Tail, Input & R, [...Result, (data: Input) => R]>:
Result
type MergeAll<T extends FunctionArray , Input = {}> =
T extends [(data: any) => infer R, ...infer Tail extends FunctionArray] ? MergeAll<Tail, R & Input>: Input
const func = <T extends [] | FunctionArray>(modifiers: T & ValidateChain<T>): MergeAll<T> => {
return (modifiers as T).reduce((acc, modifier) => {
return modifier(acc)
}, {}) as MergeAll<T>
}
let r1 = func([addA]) // Pass
let r2 = func([addA, addB]) // Pass
let r3 = func([addA, addB, updateA]) //Pass.
let r4 = func([addA, addB, updateC]) // Fails
let r5 = func([updateA]) // Fails
Oyun Alanı Bağlantısı
El kitabında koşullu türler hakkında daha fazla bilgi edinebilirsiniz
