Norse
Norse

Reputation: 648

Using keyof Type to lookup property on Type

I have the following types

type Type = {
    prop1: number;
    prop2: string;
    prop3: someOtherType
}
type Props = keyof Type

now I know you can use an "indexed access type" to get the type of propN, i.e. type Num = Type["prop1"], however I would like to use the following in a function

function(p: Props) {
    // ...
    let something = somethingElse as Type[p]
    // Typescript can't infer the type
    // ...
}

so that when i call

let a = function("prop1");
// a should be of type number

However that is not possible, and throws

'p' refers to a value, but is being used as a type here. Did you mean 'typeof p'?

however if I use typeof p, then Type[typeof p] gives me the union type number | string | someOtherType.

Is there some way to fix this. I have tried to look at using generices, but it doesn't seem possible.

Upvotes: 0

Views: 109

Answers (3)

Linda Paiste
Linda Paiste

Reputation: 42188

When you call typeof p you get the Typescript type of the Javascript variable p. You have said right here function(p: Props) that the type of p is Props. That type means that it can be any of the keys of Props.

You must use a generic if you want your code to know which key p is. Otherwise that information is discarded and the type of p is widened to Props by function(p: Props).

You can use your indexed access type assertion if the type of p is generic.

function myFunc<P extends Props>(p: P) {
    // ...
    let something = somethingElse as Type[P]
    // Typescript can't infer the type
    // ...
}

But you might not even need to assert with as if everything is typed properly.

type Type = {
  prop1: number;
  prop2: string;
}
type Props = keyof Type

function myFunc<P extends Props>(p: P) {
  const type: Type = { prop1: 0, prop2: "" };
  return type[p]; // type is inferred as `Type[P]`
}

const one = myFunc("prop1"); // type number
const two = myFunc("prop2"); // type string

Typescript Playground Link

Upvotes: 1

Owl
Owl

Reputation: 6853

More complete example:

type ValueOf<T> = T[keyof T];

type Type = {
    prop1: number;
    prop2: string;
    prop3: boolean;
}

type Props = keyof Type;

function foo<T extends Props>(p: T): Type[T] {
    let something = "";
    return something as Type[T];
}

const a = foo("prop1"); // a is number
const b = foo("prop2"); // b is string
const c = foo("prop3"); // c is boolean

Upvotes: 1

Ben Wainwright
Ben Wainwright

Reputation: 4611

So I think what you are trying to achieve can be done using a generic function like so:

function foo<T extends keyof Type>(p: T) {
    let something = somethingElse as T
}

Upvotes: 2

Related Questions