简体   繁体   中英

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..

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();

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 {
}

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;
        }
    }  
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM