2017-02-23 9 views
5

特定の基本クラスを実装するクラスのリストを取得するために、C#とまったく同じようにタイプスクリプトでリフレクションを使用できますか?タイプスクリプトのリフレクションを使用して特定の基本クラスを実装する子クラスを取得するにはどうすればよいですか?

たとえば、SnakeとHorseが基本クラスAnimalを実装しているとします。今私はAnimalを実装するクラスを取得する必要があります。我々はC#で何ができるかに似て:

C#の同等のコード:

var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal))); 

タイプスクリプトクラス:それを行うための1つの方法は、を追跡することです

class Animal { 
} 

class Snake extends Animal { 
} 

class Horse extends Animal { 
} 
+1

これは現在、TypeScriptの[非目標](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals)です。あなたが解決しようとしている実際の問題は何ですか? –

+0

TypeScriptでクラスを作成するためにhttps://frhagn.github.io/Typewriter/を使用する方法はありますか? –

+0

@PaulBullivant私はそのツールが大好きですが、彼は何か違うものを探していると思います –

答えて

0

1つのグローバル配列で作成するすべてのクラス:

var allClasses = [] 
function register(c) { 
    allClasses.push(c); 
} 
register(class Animal {}); 
register(class Snake extends Animal {}); 
register(class Horse extends Animal {}); 
register(class Car {}); 

とあなたが特定のクラスを継承するクラスを必要とするとき、ちょうどあなたが欲しいものをフィルタリング:

Object.values(allClasses).filter((c) => Animal.isPrototypeOf(c)) 

このソリューションは、活字体に固有のものではない、それはあまりにも純粋なJavaScriptで動作します。

0

SOAP Webサービスからの応答を逆シリアル化する必要がありました。最初に、既存のSOAPクライアントを使用して返されたXMLからオブジェクトを取得し、次にJSONデシリアライザ(http://cloudmark.github.io/Json-Mapping/)を使用して、このオブジェクトを必要な実際のタイプにデシリアライズしました。

返されたプロパティの1つが基本型として宣言され、実際には多数の異なるサブタイプのインスタンスが含まれる可能性があるという問題がありました。

派生型に適用するクラスデコレータを作成して解決しました。このクラス・デコレータは、基本クラスを取得し、それにメタデータを適用して派生クラスを説明します。

次のコードを理解するには、WebサービスのXMLに多形性が有効であることを示すtype属性が含まれている場合、XML - > JSONパーサーが$typeプロパティを追加することが重要です。

基本型に適用される実際のメタデータは、XMLの型名と派生型のコンストラクタです。

メタデータ登録とクラスのデコレータ:

export interface IJsonPolymorphism { 
    name: string; 
    clazz: {new(): any}; 
} 

export function RegisterForPolymorphism(name: string) { 
    return (target) => { 
     let metadata = getJsonPolymorphism(target.__proto__); 
     if (!metadata) { 
      metadata = []; 
     } 
     metadata.push({name: name, clazz: target}); 
     target.__proto__ = Reflect.metadata(jsonPolymorphismMetadataKey, metadata)(target.__proto__); 
     return target; 
    }; 
} 

export function getJsonPolymorphism(target: any): IJsonPolymorphism[] { 
    return Reflect.getMetadata(jsonPolymorphismMetadataKey, target); 
} 

は使用方法:

// The base class 
export class PropertyBase { /*...*/ } 

// The derived classes 
// 

@RegisterForPolymorphism("b:PicklistProperty") 
export class PicklistProperty extends PropertyBase { /*...*/ } 
@RegisterForPolymorphism("b:TextProperty") 
export class TextProperty extends PropertyBase { /*...*/ } 

クラスのデコレータに渡された文字列は、WebからのXML応答でtype属性の値であり、サービス。

デシリアライザのコードはこのようにそれを使用しています:

if (jsonObject["$type"]) { 
    const polymorphism = getJsonPolymorphism(clazz); 
    if (polymorphism && polymorphism.filter) { 
     const subclassDefinition = polymorphism.filter(x => x.name === jsonObject["$type"])[0]; 
     if (subclassDefinition && subclassDefinition.clazz) { 
      clazz = subclassDefinition.clazz; 
     } 
    } 
} 

基本的には、clazzはにJSONオブジェクトをデシリアライズし、我々は派生型のコンストラクタでそれを交換するタイプのコンストラクタです。

このコードでは、メタデータを直接ベースクラス、階層全体に追加するという制限があります。しかし、これはRegisterForPolymorphismのコードを調整してツリーを歩くことで簡単に解決できます。

4

時代遅れの仕様に基づいているため、すぐに変更される可能性の高い機能に依存したい場合や、クラスではなくインターフェイスのみを使用する場合は、デコレータ。ここで

は一例です。

階層tracked.ts

export default function hierarchyTracked(target: new (...args: any[]) => object) { 
    for (const proto of walkPrototypeChain(target)) { 
    if (!Object.hasOwnProperty.call(proto, 'extendedBy')) { 
     const extendedBy: typeof Function.extendedBy = []; 
     Object.defineProperty(proto, 'extendedBy', { 
     get:() => extendedBy 
     }); 
    } 
    // ! is used to suppress a strictNullChecks error on optional. 
    // This is OK since we know it is now defined. 
    proto.extendedBy!.push(target); 
    } 
} 

declare global { 
    interface Function { 
    // Declared as optional because not all classes are extended. 
    extendedBy?: Array<new (...args: any[]) => object>; 
    } 
} 

function* walkPrototypeChain(target: new (...args: any[]) => object) { 
    let proto = Reflect.getPrototypeOf(target); 
    while (proto && proto !== Object) { 
    yield proto; 
    proto = Reflect.getPrototypeOf(proto); 
    } 
} 

animals.ts

import hierarchyTracked from './hierarachy-tracked'; 

export class Animal { 
    alive = true; 
    static get slayable() {return true;} 
    static extinct = false; 
} 

@hierarchyTracked export class Snake extends Animal { 
    isEctotherm = true; 
} 

@hierarchyTracked export class Cobra extends Snake { 
    isDeadly = true; 
} 

@hierarchyTracked export class Horse extends Animal { 
    isEndotherm = true; 
} 
// logs 
Animal.extendedBy && Animal.extendedBy.map(Taxon => Taxon.name) 
    .forEach(name => { 
    console.log(name); 
    }); 
// Snake 
// Cobra 
// Horse 

Snake.extendedBy && Snake.extendedBy.map(Taxon => Taxon.name) 
    .forEach(name => { 
    console.log(name); 
    }); 
// Cobra 

グローバル状態とそれに頼る必要はありません実際にはきちんと整頓され明示的です。

TypeScriptを使用していない場合はBabel 7と同じように動作します。 、あなたのコード例に戻る

import trackHierarchy from './hierarachy-tracked'; 

export class Animal { } 

class Snake extends Animal { ... } 
trackHierarchy(Snake); 

export {Snake}; 

を上記:あなたはデコレータに依存したくない場合は、これは、手動で書くのは簡単です。もちろん、

(上記のデコレータの使用に関する同じ注意点がまだ適用されることに注意してください)それは容易に達成される。

それはあなた自身がこのようなコードを書くことを望む見つけた場合、あなたが戻って一歩を踏み出す必要があります

を警告するだけで

const childClasses = Animal.extendedBy || []; 

A言葉に

var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal))); 

から行きますJavaScriptを実際に知っていることを確認してください。このようなパターン、実際にはユースケースの例は、通常、誰かが古典的な考え方、つまりES 2015のクラスに気づいたことを伝え、それらが伝統的な言語のクラスに関係していると考えるようになりました。

ESクラスは、C#、C++、Java、Scalaのようなクラスではありません。

JavaScriptのクラスはで、ではありません。

JavaScriptのクラスはの値です。

これらの宣言フォームは基本的にはプロトタイプにまさる構文上の砂糖です。達成しようとしているパターンは、あなたがこれをよく理解していないかもしれないことを示唆しています。特に、それは彼らが特別なだと考えるかもしれないことを示唆します。

+0

これは、デコレータの大幅な変更を行った場合でも、Babel 7で動作します。もちろん、あなたが安全な側にいたいなら、関数形式を使うだけです –

関連する問題