TypeScript - passing a fieldname of an interface in a type-safe manner

Viewed 107

I have:

interface MyInterface {
    a: string;
    b: number;
}

I have another general-purpose function:

myFunction(myInstance: MyInterace, fieldName: string) {
   let field = myInstance[fieldName];

   // do something with this field
}

Usage being:

myFunction(myInst, "a");
or
myFunction(myInst, "b");

However, I would like to pass "a" or "b" in a type-safe manner. That means that if I were to change MyInterace a field to a2, the call used in myFunction(myInst, "a") wouldn't compile and I would be able to find in compilation time all these places that got broken due to renaming.

I tried using Pick but couldn't get the name of the field from it.

Any suggestions? I would imagine that there should be some:

MyInterace::a from which I could extra the string "a".
1 Answers

You just need to declare fieldName as type keyof MyInterface

function myFunction(myInstance: MyInterface, fieldName: keyof MyInterface) {
   let field = myInstance[fieldName];
   // do something with this field
}

myFunction({ a: 'a', b: 1 }, 'a')
myFunction({ a: 'a', b: 1 }, 'b')
myFunction({ a: 'a', b: 1 }, 'c') // type error

Playground


Or as a fancier version, you can make this generic and then it will work with any type, and not just MyInterface.

Now you pass any type to myInstance, and fieldName is required to be a keyof whatever that type is.

function myFunctionGeneric<T>(myInstance: T, fieldName: keyof T) {
  let field = myInstance[fieldName];
  // do something with this field
}

myFunctionGeneric({ foo: 'a', bar: 1 }, 'foo')
myFunctionGeneric({ foo: 'a', bar: 1 }, 'bing') // type error

Of course only use this generic version if you need to, since it introduces complexity for something you might not need at all.

Playground

Related