Skip to content

Instantly share code, notes, and snippets.

@Jimbly
Last active October 19, 2022 21:35
Show Gist options
  • Save Jimbly/99a1ec971f22adf2875571cddc7f9b4d to your computer and use it in GitHub Desktop.
Save Jimbly/99a1ec971f22adf2875571cddc7f9b4d to your computer and use it in GitHub Desktop.
allowDeclareFields example and commentary
// The setup: I have an Entity class hierarchy for server/common/client, and each entity has
// a reference to an entity_manager, which should be templated to the appropriate type so
// that an entity getting other entities from the manager always get the correct type of
// entity back.
// The common code:
interface EntityManager<Entity extends EntityCommon = EntityCommon> {
entities: Partial<Record<number, Entity>>;
}
class EntityCommon {
manager: EntityManager;
constructor(manager: EntityManager) {
this.manager = manager;
}
}
// Then, how do we declare our server entity such that `manager` is the correct type?
// Under current settings, I used a "definite assignment assertion operator" to avoid the error about
// "not assigned definitively in the constructor":
class EntityServer extends EntityCommon {
manager!: EntityManager<EntityServer>;
constructor(manager: EntityManager<EntityServer>) {
super(manager);
}
}
// But, seems the more correct approach is to enable allowDeclareFields, and just
// declare the type of the field:
class EntityServer extends EntityCommon {
declare manager: EntityManager<EntityServer>;
constructor(manager: EntityManager<EntityServer>) {
super(manager);
}
}
// However, code generation changes with this option enabled... the "current"
// code above generates this beautiful code:
function EntityCommon(manager) {
this.manager = manager;
}
function EntityServer(manager) {
EntityCommon.call(this, manager);
}
// With allowDeclareFields on, this code would now, unfortunately, generate this,
// which is both longer, and functions differently (however, it more closely
// matches how ES class inheritance works, indicating the "current" code is
// just "wrong").
function EntityCommon(manager) {
this.manager = undefined;
this.manager = manager;
}
function EntityServer(manager) {
EntityCommon.call(this, manager);
this.manager = undefined; // !!! Overwriting the desired value!
}
// Adjusting to use `declare`, the child class's field gets stripped entirely
// by typescript, and then generates this correct, if wordy, code:
function EntityCommon(manager) {
this.manager = undefined;
this.manager = manager;
}
function EntityServer(manager) {
EntityCommon.call(this, manager);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment