2017-08-24 15 views
0

私の関数memoize(fn)は、fnと同じ型の別の関数を返すようにします。バリエーションは種類が入りますか?

私は以下のような醜い解決策を持っています:
variadic genericsを書く正しい方法は?

export const memoize = <FN>(fn: FN) : FN => { 
    const cache = { }; 
    const run : any = (...args) => { 
    const key = JSON.stringify(args); 
    if(!cache[key]) { 
     cache[key] = (fn as any)(...args).catch(up => { 
     delete cache[key]; 
     throw up; 
     }); 
    } 
    return cache[key]; 
    }; 
    return run as FN; 
} 

const get = memoize((url: string) => fetch(url, {method: 'GET'})); 
+0

私はあなたがこれよりも良くなるとは思わない。何が問題なの?基本的には、関数を取り、同じ型の関数を返す関数を定義しています。あなたが望んでいたものではありませんか? – toskv

+0

@toskvしかし、私は関数のパラメータ型にアクセスする必要はありません。 「<...ARGS, R>(fn:(... ARGS)=> R)=> {'? –

+0

私が知る限り、パラメータとして受け取るランダム関数の引数の型を取得する方法はありません。 :( – toskv

答えて

4

残念ながら、タイプスクリプトでは現在variadic kindsはサポートされていません。幸いにも、あなたはおそらくそれを必要としません。

機能には2つの側面があります。発信者が使用するシグネチャである「outside」と、実装である「inside」です。理想的には、シグネチャで外部の呼び出し元を関数の安全な使用法に厳密に制限し、同時に関数の内部実装も安全であることをTypeScriptで確実にしたいとします。

memoizeは、プロミス(右?)を返す任意のタイプのパラメータの任意の数の関数を受け取り、同じタイプの関数を返すようにしたいと思われます。あなたの既存の署名<FN>(fn: FN) : FNは "同じタイプを返す"部分を取得しますが、他には何もしません。したがって、たとえば、何もこれをやってから呼び出し元を停止しない:

const bad = memoize((x: string)=>x+"!"); // runtime explosion, no .catch() 
const veryBad = memoize("whoops"); // runtime explosion, not a function 

ここだけの関数の右側の種類を入力することを可能にする署名です:

export const memoize = <FN extends (...args: any[]) => Promise<{}>>(fn: FN): FN => { 
    // ... same implementation 
} 

今、発信者が幸せになります:

const get = memoize((url: string) => fetch(url, { method: 'GET' })); // okay 
const getBody = memoize((url: string, body: any) => fetch(url, { method: 'GET', body: body })); // okay 
const bad = memoize((x: string) => (x + "!")); // error: string is not a Promise 
const veryBad = memoize("whoops"); // error: "whoops" is not a function 

これにより、実装の安全性が確保されます。今のところ、あなたはany(そこに暗黙のうちにanyがある)を主張することに頼っています。今では活字体はfnPromiseを返し、あなたはそれらのアサーションの一部を緩和することができることを知っている:

export const memoize = <FN extends (...args: any[]) => Promise<{}>>(fn: FN): FN => { 
    const cache: { [k: string]: Promise<{}> } = {}; // holds promises 
    const run = (...args: any[]) => { 
    const key = JSON.stringify(args); 
    if (!cache[key]) { 
     // fn doesn't have to be any to typecheck 
     cache[key] = fn(...args).catch(up => { 
     delete cache[key]; 
     throw up; 
     }); 
    } 
    return cache[key]; 
    }; 
    return run as FN; 
} 

活字体が知っているすべてはFNはの型のサブタイプであるということですので、あなたはまだ、runFN型であることを主張する必要がありますrunではなく、のタイプrunです。あなたは余分な性質を持つ関数に渡すことができ、そして、あなたはまた、余分なプロパティを返します不当な主張をしている:あり、このための良い理由がある

const crazyFunction = Object.assign((url: string) => fetch(url, { method: 'GET' }), { color: 'purple' }); 
crazyFunction('blah'); 
console.log(typeof crazyFunction.color); // string 
const whoops = memoize(crazyFunction); 
console.log(typeof whoops.color); //TS says string, but is undefined!! 

私はあなたのことを推測するつもりですmemoizeを呼び出す前に、機能に奇妙なことをする人に何が起こっても気にしないでください。特にその人はおそらくあなたであり、あなたがそれをしないことを知っているからです。これはあなたのために十分かもしれません。


あなたが本当に実装とコールサインが真に安全にしたい場合、あなたは私たちが活字体ではありません可変長の種類を、必要かもしれません。

type Func<R, A1, A2, A3, A4, A5, A6, A7, A8, A9> = (a1: A1, a2?: A2, a3?: A3, a4?: A4, a5?: A5, a6?: A6, a7?: A7, a8?: A8, a9?: A9) => R; 
export const memoize = <R, A1=never, A2=never, A3=never, A4=never, A5=never, A6=never, A7=never, A8=never, A9=never>(fn: Func<Promise<R>, A1, A2, A3, A4, A5, A6, A7, A8, A9>): Func<Promise<R>, A1, A2, A3, A4, A5, A6, A7, A8, A9> => { 
    const cache: { [k: string]: Promise<R> } = {}; 
    const run : Func<Promise<R>, A1, A2, A3, A4, A5, A6, A7, A8, A9> = (a1,a2,a3,a4,a5,a6,a7,a8,a9) => { 
    const key = JSON.stringify([a1,a2,a3,a4,a5,a6,a7,a8,a9]); 
    if (!cache[key]) { 
     cache[key] = fn(a1,a2,a3,a4,a5,a6,a7,a8,a9).catch(up => { 
     delete cache[key]; 
     throw up; 
     }); 
    } 
    return cache[key]; 
    }; 
    return run; 
} 
const get = memoize((url: string) => fetch(url, { method: 'GET' })); // okay 
get('hello') // okay 
get('hello', 2); // error, 2 is not assignable to undefined 

をしかし、それはあなたのためのやり過ぎかもしれません:あなたは、引数の一部大きいが有限の数までの機能を受け入れることにより、それまでの偽の、9を言うことができます。


希望します。がんばろう!

関連する問題