Home > Mobile >  extract property names in a strongly typed way
extract property names in a strongly typed way

Time:09-17

The following answer addresses how to safely extract property names from a type. What this answer accomplishes is that, indeed, if there is a mistyping you get a compile-time error, not a runtime error:

https://stackoverflow.com/a/42516869/274677

However, the property names extracted in this manner are typed as string, not as a string literal (with the name of the actual property). This has some limiting implications, e.g. it does not allow them to be used with the Omit utility type.

This typescript playground link demonstrates the problem. Playground code pasted below for completeness purposes:

function propertyOf<T>(name: keyof T) {
  return name;
}

type AB = {a: number, b: number};

const A = propertyOf<AB>('a');

/*
 *  Now say that I want to constuct the following type:
 *
 *    type B = {b: number}
 * 
 * ... with the qualification that I want to construct it by omitting 
 *     some properties from a wider type. `A` cannot be used for this
 *     purpose so one has to again type the name of the property as: 'a'
 * 
 */

type B_by_omit = Omit<AB, 'a'>       // type of B_by_omit is correctly pronounced as {b: number}

type B_by_omit2 = Omit<AB, typeof A> // type of B_by_omit2 is {}
 

Is there way to safely extract property names from a type and have them typed as string literals?

CodePudding user response:

If you want propertyOf to return the string literal type "a" when you pass "a" to it, we will need to add another generic type K to the function which will be inferred by the parameter name.

// this does not work. see below
function propertyOf<T, K extends keyof T>(name: K): K {
  return name;
}

But this does not work. The problem is that TypeScript does not support partial type inference yet. So we can't explicitly provide a type for T while having K automatically inferred.

To achieve something similar, we could use currying instead.

function propertyOf<T>() {
  return <K extends keyof T>(name: K) => name;
}

The function would now be called like this:

const A = propertyOf<AB>()("a");
//    ^? const A: "a"

Playground

  • Related