2017-10-13 7 views
2

keyof私はargs.valueif内部スコープの種類を推測するコードを書くのに苦労していますこれは、しかし、それは正しく感じていません:は、コールバック引数のプロパティタイプを推測し、

function getValue<T, K extends keyof T>(value: T[keyof T], key: K): T[K] { 
    return value; 
} 

// Usage: 
if (args.field === "id") { 
    let id: number = getValue<Foo, "id">(args.value, args.field); // Correct type. 
    // Can also be used as: getValue<Foo, "id">(args.value, "id"); 
} 

任意のアイデア?ソリューションは、このようなgetValue<Foo, "id">(args.value)getValue(args.value, args.field)

+0

これはあなたに役立つかもしれませんhttps://stackoverflow.com/questions/40249906/using-a-generic-type-argument-with-typeof-t –

答えて

1

(可能であれば)として、私は本当に多くのクリーンな方法でそれを使用できるようにしたいと思いヘルパー関数を使用する必要があっても私はそれがヘルパー関数なしで行うことができるとは思いません - タイプスクリプト型の推論では、fieldvalueのタイプが相互に依存していることを考慮していません。

ですから、明示的に型の関係を表現するためにuser-defined type guard functionいわゆるを使用する必要があります。

class Foo { 
    public id: number; 
    public name: string; 
    public birth: Date; 
} 

interface ISetEventArgs<T> { 
    field: keyof T; 
    value: T[keyof T]; 
} 

function bind<T>(obj: T, event: "set", handler: (args: ISetEventArgs<T>) => void): void { 
    // Void 
} 

let f: Foo = new Foo(); 

// type guard 
function argsForField<T, F extends keyof T>(args: ISetEventArgs<T>, field: F): 
     args is { field: F; value: T[F]} { 
    return args.field === field; 
} 

bind<Foo>(f, "set", (args: ISetEventArgs<Foo>): void => { 
    if (argsForField(args, "id")) { 
     let id: number = args.value; //no error 
    } 
    else if (argsForField(args, "name")) { 
     let name: string = args.value 
    } 
    else if (argsForField(args, "birth")) { 
     let birth: Date = args.value; 
    } 
}); 
+0

ありがとうございます@artem!それはそれらの遅れている問題の1つでした:) – vladeck

0

この質問は一日中、私を悩ませてきたので、私はそれを周りに演奏し、私が持っていないながら、解決策(悲しいことに)、私はあなたの正確なユースケースに応じて、あなたに役立つかもしれないし、役に立たないかもしれないいくつかの面白い振る舞いを発見しました。

TL; DR:Fooの特定のケースでは、一般的ではないが、あなたが望むものを得ることができます。それはタイスクリプトの部分に限界があるようです。

だから、最初、一緒にISetEventArgsのフィールドと値をバインドすることができます:

interface ISetEventArgs<T, K extends keyof T> { 
    field: K; 
    value: T[K]; 
} 

は今、問題がタイプです:

ISetEventArgs<Foo, "id"|"name|"birth">

ISetEventArgs<Foo, keyof Foo>

はに解決

私たちはそれになりたい:第二の場合には、我々はtypescriptですの判別組合の機能を利用することができますので

ISetEventArgs<Foo, "id"> | ISetEventArgs<Foo, "name"> | ISetEventArgs<Foo, "birth">

。これらは意味的には同じように見えますが、タイプスクリプトは2番目のケースのみを絞り込みます。だから私たちはそれをその形にするためにいくつかのタイプの偽善をする必要があります。

そこで、我々は、タイプ定義する場合:

type FooArgs = {[K in keyof Foo]: ISetEventArgs<Foo, K>}[keyof Foo]

をし、我々はそれがどのようなタイプで動作するように、このパターンを拡張しようとする場合には、悲しいことに、私たちが望むものに解決...しかし:

type GenericArgs<T> = {[K in keyof T]: ISetEventArgs<T, K>}[keyof T]; 
type GenricFooArgs = GenericArgs<Foo>; 

突然、GenericFooArgsは、上記の最初のタイプに解決されます。 FooArgsを手動で宣言する理由が、GenericArgs<Foo>を使用する方法とは違うのは分かりません。

したがって、ISetEventArgs<T>の代わりにFooArgsを使用すると、ハンドラを実装するときに必要なものが得られます。しかし...あなたはbindの一般的な能力を失ってしまったので、結局は価値のある取引ではないかもしれません。

+1

これは[この問題]と非常に似ています(https://stackoverflow.com/questions/45883855/reason-for-typof-t-in-generics-それは固定されているはずです(https://github.com/Microsoft/TypeScript/pull/18042)。しかし、私はどのバージョン - 2.5または2.6。 – artem

+0

@CRiceこれに関するあなたのご意見ありがとうございます。 – vladeck

関連する問題