Jeśli chcesz uzyskać zależność od funkcji, która prawdopodobnie wkrótce się zmieni, ponieważ jest oparta na przestarzałej specyfikacji, a jeśli chcesz używać tylko klas, a nie interfejsów, możesz to zrobić, używając dekorator.
Oto przykład:
hierarchy-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
Nie ma potrzeby uciekać się do stanu globalnej i to jest całkiem uporządkowany i wyraźny.
Działa to również z Babel 7, jeśli nie korzystasz z TypeScript. (Zauważ, że te same zastrzeżenia dotyczące użytkowania dekorator wymienione powyżej nadal obowiązują)
Oczywiście to jest trywialne napisać ręcznie, jeśli nie chcesz polegać na dekoratorów:
import trackHierarchy from './hierarachy-tracked';
export class Animal { }
class Snake extends Animal { ... }
trackHierarchy(Snake);
export {Snake};
powrotem do kodu przykładu powyżej, łatwo to osiągnąć.
To idzie z
var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
po prostu
const childClasses = Animal.extendedBy || [];
Słowo ostrzeżenia
Jeśli znajdziesz się chcąc napisać kod tak, należy zrobić krok do tyłu i upewnij się, że znasz JavaScript. Ten rodzaj wzorca, a nawet przykład użycia, zwykle wskazuje, że ktoś przyszedł do języka z klasycznym nastawieniem, zauważył klasy ES 2015 i zaczął myśleć, że są one powiązane z klasami w językach tradycyjnych.
Klasy ES nie mogą być mniej podobne do klas C#, C++, Java lub Scala.
Przede wszystkim: Klasy w JavaScript to typy , a nie.
Klasy w JavaScript to wartości.
Ich deklaracja jest zasadniczo tylko syntaktycznym cukrem nad prototypami. Wzór, który próbujesz osiągnąć, sugeruje, że możesz tego nie rozumieć dobrze. W szczególności sugeruje, że możesz pomyśleć, że są one specjalne.
To jest obecnie [poza golem] (https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals) dla TypeScript. Jaki jest faktyczny problem, który próbujesz rozwiązać? –
A może za pomocą https://frhagn.github.io/Typewriter/ utworzyć swoje klasy w TypeScript? –
@PaulBullivant Uwielbiam to narzędzie, ale myślę, że on szuka czegoś innego –