2017-12-14 16 views
1

と呼ばれる私はtypescriptですがタイピングは機能のオブジェクトがあるパターンとによってオブジェクトに機能を見上げ機能call(name, arg)で動作するようにしようとしていますnameとし、それをargと呼びます。型推論マップから解決し、間接的に

は、私は関数に名前をマップするオブジェクトがあるとします。

interface Registry { 
    str2numb: (p: string) => number 
    num2bool: (p: number) => boolean 
} 

const REGISTRY: Registry = { 
    str2numb: p => parseInt(p, 10), 
    num2bool: p => !!p, 
} 

私もREGISTRYから機能を解決し、pでそれを呼び出す関数call(name, p)を持っています。私はタイプからパラメータp用タイプP(とも戻り値の型R)を解決するにはどうすればよい

const call = (name, p) => REGISTRY[name](p) 

call('str2numb', 123) 
//    ^^^ Would like to see an error here 

:今、私は、無効な引数が提供されている場合、それは文句ことなので、機能を入力したいですRegistry.str2numb?それも可能ですか?

// How can I resolve P and R here? 
// The resolved function is Registry[N] 
// I have tried Registry[N]<P, R> but that doesn't work :-(
const call = <N extends keyof Registry>(name: N, p: P): R => REGISTRY[name](p) 

私はここまで得ているが、それは動作しません。しかしこれは動作します

type Fn<P, R> = (p: P) => R 

const call = 
    <N extends keyof Funcs, F extends Funcs[N] & Fn<P, R>, P, R> 
    (name: N, p: P): R => 
     REGISTRY[name](p) 

call('str2numb', 123) 
//    ^^^ No error here 

// This just returns the resolved function 
const call1 = <N extends keyof Funcs>(name: N) => REGISTRY[name] 

// The type of the returned function is correctly inferred from the name 
call1('str2numb')(123) 
//    ^^^ Argument of type '123' is not assignable to parameter of type 'string' 

答えて

0

からパラメータタイプを 'アウト抽出' する方法はありませんtypescriptの関数型です。

追加作業をしたい場合は、パラメータの型と戻り値の型を別々にエンコードするデータ構造体を使用して、レジストリの型を定義できます。そのデータ構造は、実行時に使用されるが、唯一の型推論を行うには、コンパイラのためのガイドとして機能しますので、あなたが持つことができますされていない、あなたのcallタイプ-確認:私は基本的に@artemに同意し、掲示しています

// used to encode parameter and result type 
// like this: param<string>().result<number>() 
function param<P>(): { result<R>(): {p: P[], r: R[]}} { 
    return { 
     result<R>() { 
      return {p: [], r: []} // use empty arrays so we don't have 
            // to provide values 
     } 
    } 
} 

const registryTypes = { 
    str2numb: param<string>().result<number>(), 
    num2bool: param<number>().result<boolean>() 
} 
type RegistryTypes = typeof registryTypes; 

// this has the same type as `interface Registry` in the question 
type Registry = {[N in keyof RegistryTypes]: (p: RegistryTypes[N]['p'][0]) => RegistryTypes[N]['r'][0]}; 


const REGISTRY: Registry = { 
    str2numb: p => parseInt(p, 10), 
    num2bool: p => !!p, 
} 

let call: <N extends keyof RegistryTypes>(n: N, p: RegistryTypes[N]['p'][0]) => RegistryTypes[N]['r'][0]; 

const n = call('str2numb', '2'); // ok, n is a number 
const n1 = call('str2numb', 2); // error 
1

完全性のための類似しているが同一ではないこのソリューション:

// type for the compiler 
type RegistrySchema = { 
    str2numb: { argument: string, result: number }; 
    num2bool: { argument: number, result: boolean }; 
} 

// represent Registry in terms of RegistrySchema 
type Registry = { 
    [K in keyof RegistrySchema]: 
    (argument: RegistrySchema[K]['argument']) => RegistrySchema[K]['result'] 
} 

// same REGISTRY as before 
const REGISTRY: Registry = { 
    str2numb: p => parseInt(p, 10), 
    num2bool: p => !!p, 
} 

// call can be defined thusly 
function call<K extends keyof RegistrySchema>(
    k: K, 
    argument: RegistrySchema[K]['argument'] 
): RegistrySchema[K]['result'] { 
    return REGISTRY[k](argument); 
} 

// it works 
const x = call('str2numb', 123); // error 
const y = call('str2numb', "hello"); // y is number 

幸運を祈る!