Anwesh Budhathoki
Anwesh Budhathoki

Reputation: 121

Getting type of Object's value from key

I created a data.

type PenType = {
    color: string
}

type PencilType = {
    darkness: string
}

const data: {
    pen: PenType,
    pencil: PencilType
} = {
    pen: {
        color: "blue",
    },
    pencil: {
        darkness: "2B"
    }
};

type DataKeys = keyof typeof data;

I now have Object data with keys and values. I did create a function to get value from the data.

type MyReturnType = PenType | PencilType;

const getMyData = (keyToGet: DataKeys): MyReturnType => {
    return data[keyToGet];
}

const finalData = getMyData('pen');

console.log(finalData);

This works completely fine. But the issue is, I want to get correct type in my finalData. Since I have tried to access pen, I want return type to be PenType, not PenType | PencilType

Is there any way to achieve this?

Sandbox link: https://codesandbox.io/s/typescript-playground-export-forked-hnle7p?file=/index.ts

Upvotes: 2

Views: 543

Answers (1)

jcalz
jcalz

Reputation: 328262

If you explicitly annotate the return type to be PenType | PencilType, then that's what the function will return no matter what.

If, instead, you want the compiler to keep track of the literal type of the value passed in as keyToGet, you need getMyData() to be generic in that type, with a generic type parameter as follows:

const getMyData = <K extends DataKeys>(keyToGet: K) => {
    return data[keyToGet];
}

Here, getMyData is generic in K, a type parameter constrained to be assignable to DataKeys, and the type of the keytoGet function parameter.

I didn't annotate the return type explicitly. Instead, performing the indexing operation data[keyToGet] and returning it means the returned type is inferred by the compiler to be a generic indexed access type:

/* {
    pen: PenType;
    pencil: PencilType;
}[K] */

So when you call getMyData('pen'), K is inferred as "pen", and then the return type will be {pen: PenType; pencil: PencilType}["pen"], an indexed access type that evaluates to PenType:

const finalData = getMyData('pen');
// const finalData: PenType

console.log(finalData.color.toUpperCase()); // "BLUE"

Playground link to code

Upvotes: 2

Related Questions