Search code examples
typescripttypescript-typingsmapped-types

Is it possible to get the value (not key) in a literal object as a literal, not its type (such as string) via mapped type in Typescript?


Below, I have a literal object {a:"a"}. I want to extract the value of the object as a literal "a", not its type string, so as to check if some other variables is "a" and not any other string.

However if I use mapped type I only get string, not "a".

Is there a way to get the value as a type?

const a = {a:"a"}
type trueIfLiteral<T extends string>= string extends T?false:true;

type key = keyof typeof a;
const t1 : trueIfLiteral<key> = true; // key is literal

type val = typeof a[key];
const t2 : trueIfLiteral<val> = false; // val is string

So the question is simply, how can I derive a type:

type valAsLiteral = "a"

Solution

  • First, to make the object slightly less confusing, let's call it obj with properties key and val:

    const obj = { key: "val" };
    

    A problem here is that the object is typed as { key: string }. TypeScript has automatically widened the 'val' from 'val' to a string. If you want to prevent that widening, you can declare the object as const:

    const obj = { key: "val" } as const;
    

    This results in your code working, and

    type val = typeof obj[key];
    

    produces 'val' (and const t2 : trueIfLiteral<val> = false; // val is string fails, because val is no longer a generic string, but only val)

    To get the value in the emitted JS, not in TS, you can do

    const val = Object.values(obj)[0];
    

    producing 'val', typed as 'val', with no type widening.