Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get type of generic parameter

I wrote small function for better handling with types.

function evaluate(variable: any, type: string): any {
    switch (type)
    {
        case 'string': return String(variable);
        case 'number': return isNumber(variable) ? Number(variable) : -1;
        case 'boolean': {
            if (typeof variable === 'boolean')
                return variable;

            if (typeof variable === 'string')
                return (<string>variable).toLowerCase() === 'true';

            if (typeof variable === 'number')
                return variable !== 0;

            return false;
        }
        default: return null;
    }
}

function isNumber(n: any): boolean {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

I try same with generics, but don't know how to get type from generic parameter. It´s possible?

like image 867
Matěj Pokorný Avatar asked Aug 13 '13 17:08

Matěj Pokorný


People also ask

How do you indicate that a class has a generic type parameter?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

How do you find the type of generic object?

If you call the GetGenericTypeDefinition method on a Type object that already represents a generic type definition, it returns the current Type. An array of generic types is not itself generic. In the C# code A<int>[] v; or the Visual Basic code Dim v() As A(Of Integer) , the type of variable v is not generic.

What is a generic type parameter?

Generic Methods A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.


2 Answers

You cannot eliminate the type string, but you can make your function a lot more intelligent and usable in regards to types by adding overloads:

function evaluate(variable: any, type: 'string'): string;
function evaluate(variable: any, type: 'number'): number;
function evaluate(variable: any, type: 'boolean'): boolean;
function evaluate(variable: any, type: string): unknown {
    ...
    default: throw Error('unknown type');
}
const myBool = evaluate('TRUE', 'boolean'); // myBool: boolean
const myNumber = evaluate('91823', 'number'); // myBool: boolean
evaluate('91823', 'qwejrk' as any); // RUNTIME ERROR (violated types)

const mysteryType = 'number' as 'boolean' | 'number';
const myMystery = evaluate('91823', mysteryType); // COMPILER ERROR, no overload matches.

Playground Link

Note that there is no longer a null case, since it is impossible to know if an unknown string type might actually be containing a valid value like 'number' at compile-time.

This will be good enough for most people.


However...

Note above that the mysteryType union does not work. If you really really really want that to work for some reason, you can use conditional types instead:

function evaluate<T extends string>(variable: any, type: T):
    T extends 'string' ? string :
    T extends 'number' ? number :
    T extends 'boolean' ? boolean :
    never;
function evaluate(variable: any, type: string): unknown {
    ...
    default: throw Error('unknown type');
}
const mysteryType = 'number' as 'boolean' | 'number';
const myMystery = evaluate('91823', mysteryType); // myMystery: number | boolean

Playground Link


Aditionally, if you Googled this question and are wondering how to get T from MyClass<T>, that is possible as well:

class MyClass<T> {}

type GetMyClassT<C extends MyClass<any>> = C extends MyClass<infer T> ? T : unknown;
const myInstance = new MyClass<"hello">();
let x: GetMyClassT<typeof myInstance>; // x: "hello"

Playground Link

like image 135
Mingwei Samuel Avatar answered Oct 17 '22 23:10

Mingwei Samuel


typeof is a JavaScript operator. It can be used at run time to get the types JavaScript knows about. Generics are a TypeScript concept that helps check the correctness of your code but doesn't exist in the compiled output. So the short answer is no, it's not possible.

But you could do something like this:

class Holder<T> {
    value: T;
    constructor(value: T) {
        this.value = value;
    }
    typeof(): string {
        return typeof this.value;       
    }
}

Try it out.

This works because I'm operating on the value inside Holder, not on the Holder itself.

like image 33
Jeffery Grajkowski Avatar answered Oct 18 '22 01:10

Jeffery Grajkowski