残念ながら、タイプスクリプトでは現在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
がある)を主張することに頼っています。今では活字体はfn
がPromise
を返し、あなたはそれらのアサーションの一部を緩和することができることを知っている:
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
はの型のサブタイプであるということですので、あなたはまだ、run
はFN
型であることを主張する必要があります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を言うことができます。
希望します。がんばろう!
私はあなたがこれよりも良くなるとは思わない。何が問題なの?基本的には、関数を取り、同じ型の関数を返す関数を定義しています。あなたが望んでいたものではありませんか? – toskv
@toskvしかし、私は関数のパラメータ型にアクセスする必要はありません。 「<...ARGS, R>(fn:(... ARGS)=> R)=> {'? –
私が知る限り、パラメータとして受け取るランダム関数の引数の型を取得する方法はありません。 :( – toskv