Last active
October 19, 2022 21:35
-
-
Save Jimbly/99a1ec971f22adf2875571cddc7f9b4d to your computer and use it in GitHub Desktop.
allowDeclareFields example and commentary
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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