typir
Version:
General purpose type checking library
110 lines • 4.98 kB
JavaScript
/******************************************************************************
* 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