typestypescripttypescript1.8

Is it possible to define a type (string literal union) within a class in TypeScript?


In TypeScript 1.8, they added a new type called a "string literal type" which lets you declare a type that can only be one of a set of finite values. The example that they give is:

type Easing = "ease-in" | "ease-out" | "ease-in-out";

But, in all their examples, the type is either defined outside of the class that it is used in or defined inline without an alias. As explained (fairly tersely) in the answers to this question, type alias declarations are scoped to their containing module.

In my project, I would like to define type aliases for string literals that are paired with specific classes and used in multiple places. Because of the structure of the APIs that I am consuming, there are multiple classes which each have properties of the same name but different potential values. I'd like to keep my code neat and not have a bunch of global type declarations with prefixes (MyClassAPropertyValue, MyClassBPropertyValue, etc.) if I can help it.

So, is there any way to declare a type within class scope (preferably so that it is consumable from external modules)? If not, is there a close substitute or planned feature that will fill this use case?


Use case:

Suppose I have two classes that wrap my underlying API data. Each class represents different data, they just happen to have some properties with the same name. I want a way of defining properties like so:

export class APIObjectA {
    // Valid values in APIObjectA
    public type Property1Value = "possible_value_A" | "possible_value_B";
    public type Property2Value = "possible_value_C" | "possible_value_D";

    // Here, "Property1Value" refers to the version in APIObjectA
    public get property1(): Property1Value {
        // Return value of property1 from underlying API data
    }

    public get property2(): Property2Value {
        // ...
    }
}

export class APIObjectB {
    // Valid values in APIObjectB
    public type Property1Value = "possible_value_E" | "possible_value_F";
    public type Property2Value = "possible_value_G" | "possible_value_H";

    // In this context, "Property1Value" refers to the version in APIObjectB
    public get property1(): Property1Value {
        // ...
    }

    public get property2(): Property2Value {
        // ...
    }
}

And then, ideally, I'd be able to refer to a specific type in consuming code so that I have types everywhere:

var myNewValue: APIObjectB.Property1Value = "possible_value_F";

Solution

  • You can't define types (whether they be nested classes, enums or string literal types) directly inside a class.

    But, as shown in this answer, you can get around this by defining the nested type in a module of the same name.

    export class APIObjectA {
        // Here, "Property1Value" refers to the version in APIObjectA
        public get property1(): APIObjectA.Property1Value {
            // Return value of property1 from underlying API data
        }
    
        public get property2(): APIObjectA.Property2Value {
            // ...
        }
    }
    
    module APIObjectA {
        // Valid values in APIObjectA
        export type Property1Value = "possible_value_A" | "possible_value_B";
        export type Property2Value = "possible_value_C" | "possible_value_D";
    }
    
    export class APIObjectB {
        // In this context, "Property1Value" refers to the version in APIObjectB
        public get property1(): APIObjectB.Property1Value {
            // ...
        }
    
        public get property2(): APIObjectB.Property2Value {
            // ...
        }
    }
    
    module APIObjectB {
        // Valid values in APIObjectB
        export type Property1Value = "possible_value_E" | "possible_value_F";
        export type Property2Value = "possible_value_G" | "possible_value_H";
    }
    

    Note that you will need to use the fully qualified name for the return types of your properties. For example, you need to use APIObjectB.Property1Value instead of just Property1Value.