1

This is my class:

class CONSTANT {
    static readonly PATH = new class {
        /** private visibility because these relative paths don't make sense for direct access, they're only useful to path class
         *
         * @type {{AFTER_EFFECTS_TEMPLATE_BINARY_VERSION: module:fs.PathLike; AFTER_EFFECTS_TEMPLATE_XML_VERSION: module:fs.PathLike; RELATIVE_PATH_TO_AFTER_EFFECTS: module:fs.PathLike; OUTPUT_DIRECTORY_NAME: module:fs.PathLike; INPUT_DIRECTORY_NAME: module:fs.PathLike; ASSETS_DIRECTORY_NAME: module:fs.PathLike}}
         */
        private readonly RELATIVE = new class {
            readonly AFTER_EFFECTS_TEMPLATE_FILENAME: fs.PathLike = '\\video-template.aep';
            readonly AFTER_EFFECTS_TEMPLATE_BINARY_VERSION: fs.PathLike = `\\assets\\aep-template\\src${this.AFTER_EFFECTS_TEMPLATE_FILENAME}`;
            readonly AFTER_EFFECTS_TEMPLATE_XML_VERSION: fs.PathLike = '\\assets\\aep-template\\intermediates\\video-template.aepx';
            readonly RELATIVE_PATH_TO_AFTER_EFFECTS: fs.PathLike = '\\Adobe\\Adobe After Effects CC 2018\\Support Files\\AfterFX.exe';
            readonly OUTPUT_DIRECTORY_NAME: fs.PathLike = '\\output';
            readonly INPUT_DIRECTORY_NAME: fs.PathLike = '\\input';
            readonly ASSETS_DIRECTORY_NAME: fs.PathLike = '\\assets';
        }; 
    } 
}

I want to type the RELATIVE anonymous class to only have properties of the type fs.PathLike. Is this possible without mentioning type on each property?

PS: The reason I've this and not an object literal is because I can combine properties of the class among themselves see: https://stackoverflow.com/a/50929798/1311745

Dheeraj Bhaskar
  • 18,633
  • 9
  • 63
  • 66

1 Answers1

0

There is no way to get typescript to infer a type for a member other then from the initialization expression.

We can achieve a similar effect though, namely:

  1. The resulting object can contain only fields of type compatible with fs.PathLike (and we will get an error if we assign a type that is not compatible with fs.PathLike)
  2. Have the type of all fields be fs.PathLike regardless of the original type assigned to them (which can be string | Buffer | URL);

To do this we will use an extra function, that will take in an object whose fields are implicitly type. The type constraint on the function generic parameter will trigger an error if we pass in an object with fields that are not something compatible with fs.PathLike or a container of other paths. This function will return a new type, that will have all original field types replaced with fs.PathLike

type PathLikeContainer<T> = {
    [P in keyof T] : T[P] extends fs.PathLike ? fs.PathLike : T[P] extends object ? PathLikeContainer<T[P]> : never;
}
function asPathContainer<T extends PathLikeContainer<T>>(p: T) : PathLikeContainer<T>{
    return p;
}

class CONSTANT {
    static readonly PATH = asPathContainer(new class {
        readonly RELATIVE = new class {
            readonly AFTER_EFFECTS_TEMPLATE_FILENAME = '\\video-template.aep';
            readonly AFTER_EFFECTS_TEMPLATE_BINARY_VERSION = `\\assets\\aep-template\\src${this.AFTER_EFFECTS_TEMPLATE_FILENAME}`;
            readonly AFTER_EFFECTS_TEMPLATE_XML_VERSION = '\\assets\\aep-template\\intermediates\\video-template.aepx';
            readonly RELATIVE_PATH_TO_AFTER_EFFECTS = '\\Adobe\\Adobe After Effects CC 2018\\Support Files\\AfterFX.exe';
            readonly OUTPUT_DIRECTORY_NAME = '\\output';
            readonly INPUT_DIRECTORY_NAME = '\\input';
            readonly ASSETS_DIRECTORY_NAME = '\\assets';
            //readonly INVALID = 0; // will be an error
        }; 
    })
}


CONSTANT.PATH.RELATIVE.AFTER_EFFECTS_TEMPLATE_BINARY_VERSION //fs.PathLike
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357