Search code examples
typescripttypescript2.0typescript1.8typescript1.5

Convert a type name to the actual type


Suppose we have the following enum

enum PrimayValType {
  "string",
  "boolean",
  "integer",
  "float"
}

Now i want to write a function that inputs a Buffer and a parameter of type PrimaryValType and converts the buffer based on the PrimaryValType. How to write such a function in Typescript?

function f(b: Buffer, t: PrimayValType): ??? {
    // converts b to a literal based on t
}

const b = Buffer.from('test', 'utf-8');
f(b, PrimayValType.string) // should be of type string

Solution

  • You can write the return type of your function as a mapped type. First, define PrimaryValType as a mapping from the string literals to the actual types:

    type PrimaryValType = {
        "string": string,
        "boolean": boolean,
        "integer": number,
        "float": number,
    }
    

    Then given a string of type K extends keyof PrimaryValType, we can map it to the correct return type using the mapped type PrimaryValType[K].

    To parse the input as the right type, you can switch on the type-string:

    function parse<K extends keyof PrimaryValType>(s: string, t: K): PrimaryValType[K] {
        switch (t) {
            case "integer": return parseInt(s) as PrimaryValType[K];
            case "float":   return parseFloat(s) as PrimaryValType[K];
            case "boolean": return (s === "true") as PrimaryValType[K];
            case "string":  s as PrimaryValType[K];
    
            default: throw new Error("Illegal t: " + t);
        }
    }
    

    The type assertions are needed because Typescript can't tell that when t === 'integer' then K can't be 'string', for example. The code can be neatened by storing the parser functions in an object, as in @kmos.w's answer:

    const parsers: { [K in keyof PrimaryValType]: (s: string) => PrimaryValType[K] } = {
        "integer": parseInt,
        "float":   parseFloat,
        "boolean": s => s === "true",
        "string":  s => s,
    };
    
    function parse<K extends keyof PrimaryValType>(s: string, t: K): PrimaryValType[K] {
        return parsers[t](s) as PrimaryValType[K];
    }
    

    The type assertion is still needed, for the same reason.

    Playground Link