Rishi Tiwari
Rishi Tiwari

Reputation: 1041

Default value of Type in Generic classes in Typescript

I need to set a default value of a variable based on its type in Typescript generic classes like below

  class MyClass<T>{
      myvariable: T // Here I want to set the value of this variable 
                    // with the default value of the type passed in 'T'
    }

For example if the T is number then the default value of the variable myvariable should be "0", similarly for string it should be empty string and so on..

Upvotes: 10

Views: 12821

Answers (3)

Nitzan Tomer
Nitzan Tomer

Reputation: 164139

You can't do that as the actual type which is T will only be known at runtime.

What you can do:

abstract class MyClass<T> {
    myvariable: T;

    constructor() {
        this.myvariable = this.getInitialValue();
    }

    protected abstract getInitialValue(): T;
}

Now you just extend this class, like so:

class MyStringClass extends MyClass<string> {
    protected getInitialValue(): string {
        return "init-value";
    }
}

Edit

What you're asking for can not be done because T only exists in the typescript realm, and it doesn't "survive" the compilation process.

For example, this:

class MyClass<T> {
    myvariable: T;

    constructor(value: T) {
        this.myvariable = value;
    }
}

Compiles into:

var MyClass = (function () {
    function MyClass(value) {
        this.myvariable = value;
    }
    return MyClass;
}());

As you can see, in the compiled version there's no T, so you can't use that information at runtime in order to generate a default value.


Another solution is to have a map of default values:

var defaultValues = {
    "string": "",
    "number": 0,
    "boolean": false
}

class MyClass<T> {
    myvariable: T;

    constructor(value: T) {
        this.myvariable = value;
    }
}

let a = new MyClass<string>(defaultValues.string);
let b = new MyClass<boolean>(defaultValues.boolean);

You can also use static factory methods:

class MyClass<T> {
    myvariable: T;

    constructor(value: T) {
        this.myvariable = value;
    }

    static stringInstance(): MyClass<string> {
        return new MyClass<string>("");
    }

    static numberInstance(): MyClass<number> {
        return new MyClass<number>(0);
    }

    static booleanInstance(): MyClass<boolean> {
        return new MyClass<boolean>(false);
    }
}

let a = MyClass.stringInstance();
let b = MyClass.booleanInstance();

Upvotes: 12

iberbeu
iberbeu

Reputation: 16205

Well I guess I came up with a possible solution. Anyway I must say that Nitzan Tomer is right and the class type is not available in runtime. This is the funny side of TS :)

Here you can check the type of the objects you get in and then set a default value. You could also change the place and the objects to check in order to do it the way you want but I guess it could be a good starting point for you. Notice that you have to do a double cast because TS cannot guarantee that types are compatible. I haven't tried it yet but it compiles well

class MyClass<T>{
      myvariable: T 

    constructor(param: any) {
        if (typeof param === 'string') {
            this.myvariable = <T><any> "";
        }
        else if (typeof param === 'number') {
            this.myvariable = <T><any> 0;
        }
    }  
}

Upvotes: 0

Markus Johnsson
Markus Johnsson

Reputation: 4019

T is lost at runtime, so you need to pass in the type like a value. And you can do that by passing the constructor.

When I have a similar need, I usually use an interface like this:

interface Type<T> {
     new() : T;
}

And create MyClass like so:

class MyClass<T>{
    myvariable: T;

    constructor(type: Type<T>) {

        this.myvariable = new type();
    }
}

Then I can use MyClass like so:

let myinstance = new MyClass(TheOtherType);

It works for classes, but not for built-ins like string and number.

TheOtherType is the constructor of a class such as:

class TheOtherType {
}

Upvotes: 0

Related Questions