UNPKG

typir

Version:

General purpose type checking library

110 lines 4.98 kB
/****************************************************************************** * Copyright 2024 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ import { removeFromArray } from '../utils/utils.js'; /** * A TypeReference accepts a specification for a type and searches for the corresponding type in the type system according to this specification. * Different TypeReferences might resolve to the same Type. * This class is used during the use case, when a Typir type uses other types for its properties, * e.g. class types use other types from the type system for describing the types of its fields ("use existing type"). * * The internal logic of a TypeReference is independent from the kind of the type to resolve. * A TypeReference takes care of the lifecycle of the types. * * Once the type is resolved, listeners are notified about this and all following changes of its state. */ export class TypeReference { constructor(descriptor, services) { this.resolvedType = undefined; /** These listeners will be informed, whenever the resolved state of this TypeReference switched from undefined to an actual type or from an actual type to undefined. */ this.listeners = []; this.descriptor = descriptor; this.services = services; this.startResolving(); } dispose() { this.stopResolving(); this.listeners.splice(0, this.listeners.length); } startResolving() { // discard the previously resolved type (if any) this.resolvedType = undefined; // react on new types this.services.infrastructure.Graph.addListener(this); // react on new inference rules this.services.Inference.addListener(this); // don't react on state changes of already existing types which are not (yet) completed, since TypeDescriptors don't care about the initialization state of types // try to resolve now this.resolve(); } stopResolving() { // it is not required to listen to new types anymore, since the type is already resolved/found this.services.infrastructure.Graph.removeListener(this); this.services.Inference.removeListener(this); } getType() { return this.resolvedType; } /** * Resolves the referenced type, if the type is not yet resolved. * Note that the resolved type might not be completed yet. * @returns the result of the currently executed resolution */ resolve() { if (this.resolvedType) { // the type is already resolved => nothing to do return 'ALREADY_RESOLVED'; } // try to resolve the type const resolvedType = this.services.infrastructure.TypeResolver.tryToResolve(this.descriptor); if (resolvedType) { // the type is successfully resolved! this.resolvedType = resolvedType; this.stopResolving(); // notify observers this.listeners.slice().forEach(listener => listener.onTypeReferenceResolved(this, resolvedType)); return 'SUCCESSFULLY_RESOLVED'; } else { // the type is not resolved (yet) return 'RESOLVING_FAILED'; } } addListener(listener, informAboutCurrentState) { this.listeners.push(listener); if (informAboutCurrentState) { if (this.resolvedType) { listener.onTypeReferenceResolved(this, this.resolvedType); } else { listener.onTypeReferenceInvalidated(this, undefined); } } } removeListener(listener) { removeFromArray(listener, this.listeners); } onAddedType(_addedType, _key) { // after adding a new type, try to resolve the type this.resolve(); // possible performance optimization: is it possible to do this more performant by looking at the "addedType"? } onRemovedType(removedType, _key) { // the resolved type of this TypeReference is removed! if (removedType === this.resolvedType) { // notify observers, that the type reference is broken this.listeners.slice().forEach(listener => listener.onTypeReferenceInvalidated(this, this.resolvedType)); // start resolving the type again this.startResolving(); } } onAddedInferenceRule(_rule, _options) { // after adding a new inference rule, try to resolve the type this.resolve(); // possible performance optimization: use only the new inference rule to resolve the type } onRemovedInferenceRule(_rule, _options) { // empty, since removed inference rules don't help to resolve a type } } //# sourceMappingURL=type-reference.js.map