By Interloper


2019-03-17 17:11:04 8 Comments

I need to write generic function that takes as parameters object and key from subset of keys of object's type which correspond to the values of the specified type.

I tried to implement it as follows.

type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp ? P : never }[keyof T];

function getLen<T>(obj: T, p: KeysOfType<T, string>): number {
    return obj[p].length
}

But the compiller gives an error with message "Property 'length' does on exist on type 'T[{ [P in keyof T]: T[P] extends TProp ? P : never }[keyof T]]'".

Why does the compiler does not consider that I have limited set of possible keys only keys corresponding to a value of type string? How to fix it?

1 comments

@jcalz 2019-03-17 17:38:16

The compiler just isn't clever enough. Conditional types that depend on generic parameters (like KeysOfType<T, string>) are generally treated by the compiler as somewhat opaque, and while you understand that KeysOfType<T, V> was specifically constructed so as to make sure that T[KeysOfType<T, V>] extends V is true, the compiler doesn't even try.

The most general solution available to us in cases like this is to use a type assertion. For example, you can tell the compiler not to worry, and to treat obj[p] as a string:

function getLen<T>(obj: T, p: KeysOfType<T, string>): number {
    return (obj[p] as unknown as string).length;
    // the type T[{ [P in keyof T]: T[P] extends string ? P : never; }[keyof T]]
    // is so opaque to the compiler that we must widen to unknown 
    // before narrowing to string
}

Note that you are relieving the compiler of the duty of verifying type safety. You could just as easily had said obj[p] as unknown as boolean and the compiler would have believed you. So use this power with caution.


Another way to do a similar thing is to use a single function overload to distinguish between the generic conditional type as seen by the caller and the hopefully more tractable type as seen by the implementation:

// call signature, unchanged
function getLen<T>(obj: T, p: KeysOfType<T, string>): number;

// implementation signature... let's make p the generic type K
// and say that obj has keys K and values of type string
function getLen<K extends keyof any>(obj: Record<K, string>, p: K): number {
  return obj[p].length;
}

The reason that it's similar to a type assertion is because the compiler allows you to make the implementation signature looser than the call signatures... if you're not careful you can lie to the compiler and you won't see a problem until runtime.


Okay, hope that helps. Good luck!

Related Questions

Sponsored Content

13 Answered Questions

[SOLVED] Determine the type of an object?

21 Answered Questions

[SOLVED] Create Generic method constraining T to an Enum

13 Answered Questions

[SOLVED] What's the canonical way to check for type in Python?

  • 2008-09-30 11:00:10
  • Herge
  • 887858 View
  • 1297 Score
  • 13 Answer
  • Tags:   python types

31 Answered Questions

[SOLVED] How to create a generic array in Java?

1 Answered Questions

[SOLVED] Typescript doesn't infer value of generic object

14 Answered Questions

[SOLVED] Type Checking: typeof, GetType, or is?

7 Answered Questions

[SOLVED] What are the differences between type() and isinstance()?

9 Answered Questions

[SOLVED] What are POD types in C++?

  • 2008-09-28 18:36:09
  • paxos1977
  • 314160 View
  • 985 Score
  • 9 Answer
  • Tags:   c++ types c++-faq

16 Answered Questions

[SOLVED] How to determine a Python variable's type?

1 Answered Questions

[SOLVED] Parameter Generic Inference With keyof

Sponsored Content