2017-02-23 11 views
5

특정 기본 클래스를 구현하는 클래스 목록을 가져 오기 위해 C#과 같은 유형 스크립트에서 리플렉션을 사용할 수 있습니까?유형 스크립트에서 리플렉션을 사용하여 특정 기본 클래스를 구현하는 하위 클래스를 얻는 방법은 무엇입니까?

예를 들어, Snake와 Horse가 기본 클래스 Animal을 구현한다고 가정 해 보겠습니다. 이제 Animal을 구현하는 클래스를 가져와야합니다.

C# 동등한 코드 :

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

유형 스크립트 클래스 : 그것은 추적하는 것입니다 할

class Animal { 
} 

class Snake extends Animal { 
} 

class Horse extends Animal { 
} 
+1

현재 TypeScript 용으로 [비 목적] (https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals)입니다. 당신이 해결하려고하는 실제 문제는 무엇입니까? –

+0

https://frhagn.github.io/Typewriter/를 사용하여 TypeScript에서 클래스를 만드는 방법에 대해 알려주십시오. –

+0

@PaulBullivant 나는 그 도구를 좋아하지만 그가 뭔가 다른 것을 찾고 있다고 생각한다. –

답변

0

한 가지 방법은 우리가 C#으로 무엇을 할 수 있는지와 유사 하나의 전역 배열로 만드는 모든 클래스 :

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)) 

이 솔루션은 타이프 라이터 특정하지 않습니다, 너무 순수 자바 스크립트와 함께 작동합니다.

0

SOAP 웹 서비스의 응답을 역 직렬화해야했습니다. 먼저 기존 SOAP 클라이언트를 사용하여 반환 된 XML에서 객체를 가져온 다음 JSON 디시리얼라이저 (http://cloudmark.github.io/Json-Mapping/)를 사용하여이 객체를 원하는 실제 유형으로 비 직렬화했습니다.

반환되는 속성 중 하나가 기본 유형으로 선언되어 실제로 여러 가지 하위 유형의 인스턴스를 포함 할 수있는 문제가있었습니다.

파생 된 유형에 적용 할 클래스 데코레이터를 만들어 해결했습니다. 이 클래스 장식자는 기본 클래스를 가져오고 메타 데이터를 적용하여 파생 클래스를 설명합니다. 웹 서비스에서 XML이 다형성 플레이에 있음을 나타내는 type 속성을 포함하는 경우> JSON 파서는 $type 속성을 추가 -

는이 XML이 있음을 아는 것이 중요합니다, 다음 코드를 이해합니다.

기본 형식에 적용되는 실제 메타 데이터는 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); 
} 

사용법 : 클래스 데코레이터에 전달 웹에서 XML 응답의 type 속성의 값입니다 있습니다

// 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 { /*...*/ } 

문자열 서비스. 디시리얼라이저의

코드는 다음처럼 사용한다 :

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

구식 사양을 기반으로하므로 곧 변경 될 가능성이있는 기능에 기꺼이 의존하려는 경우 및 클래스가 아닌 인터페이스 만 사용하려는 경우 다음을 사용하여이를 수행 할 수 있습니다. 장식 자.

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 

전역 상태과에 의지 할 필요가 없습니다

계층 구조 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 : 여기

은 예입니다 실제로 꽤 깔끔하고 명백합니다.

TypeScript를 사용하지 않는 경우 Babel 7과 작동합니다. 당신의 예제 코드로 돌아 가기

import trackHierarchy from './hierarachy-tracked'; 

export class Animal { } 

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

export {Snake}; 

을 위 : 당신이 장식에 의존하지 않으려면이 수동으로 작성하는 사소한 물론

(위에서 언급 한 장식 사용에 관한 같은주의 사항이 여전히 적용 참고) 그것은 쉽게 달성된다.

그것은 당신이 자신과 같은 코드를 작성하고자 발견하면 단순히 경고

const childClasses = Animal.extendedBy || []; 

단어, 당신은 다시 걸음을해야하는

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

에서 간다 JavaScript를 실제로 알고 있는지 확인하십시오. 이러한 유형의 패턴과 실제로 사용 사례의 예는 대개 ES 2015 클래스에 주목 한 고전적 사고 방식으로 누군가가 언어에 왔음을 나타내며 전통적 언어로 클래스와 관련 있다고 생각하기 시작했습니다.

ES 클래스는 C#, C++, Java 또는 Scala 클래스와 비슷할 수 없습니다.

JavaScript의 클래스는 이 아니고 유형입니다.

JavaScript의 클래스는 값이입니다.

그들의 선언 형식은 근본적으로 프로토 타입에 대한 구문 론적 설탕입니다. 성취하려는 패턴은 당신이 이것을 잘 이해하지 못할 수도 있음을 암시합니다. 특히 그들은 특별이라고 생각할 수 있습니다.

+0

이것은 바벨 7에서 꾸준히 변화합니다. 물론 안전한면에 있기를 원한다면 함수 형식을 사용하면됩니다. –

관련 문제