Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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";
like image 232
Wasabi Fan Avatar asked Apr 03 '16 06:04

Wasabi Fan


People also ask

How do you define a string type in TypeScript?

It is a type of primitive data type that is used to store text data. The string values are used between single quotation marks or double quotation marks, and also array of characters works same as a string. TypeScript string work with the series of character. var var_name = new String(string);

Is it possible to combine types in TS?

TypeScript allows merging between multiple types such as interface with interface , enum with enum , namespace with namespace , etc.

What is the type of a string literal?

A string literal type is a type whose expected value is a string with textual contents equal to that of the string literal type. In other words: A variable of a string literal type can only be assigned the exact string value specified in the string literal type.


1 Answers

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.

like image 134
reduckted Avatar answered Oct 09 '22 19:10

reduckted