Isaac Ferreira
Isaac Ferreira

Reputation: 436

Type inference with in operator

Typescript doesn't allow infering a type to the object using in-operator.

For example,

function getKey<T extends object, U extends string> (obj: T, key: U) {
    if (key in obj) {
        return obj[key] // Type 'U' cannot be used to index type 'T'.
    }
}

Typescript should recognize that U is keyof T, so I could use it as a key.

How can i solve this without the need to using any?

Upvotes: 1

Views: 151

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

You need to declare U as extending keyof T in order to use it for indexing an object without a string indexer.

function getKey<T extends object, U extends keyof T> (obj: T, key: U) {
    if (key in obj) {
        return obj[key] // Now works!
    }
}

As the PR for in type guards suggest the guard impacts the type of T not the type of the key, so you still need to explicitly tell the compiler that U extends keyof T

If you want to keep U as string (and I would encourage you to consider if you really need it to be a type parameter and you can't just use string for the argument) you can have multiple signatures for the function :

// The visible signatures of the function 
// The first one takes keyof T and infers return type based on it
function getKey<T extends object, U extends keyof T> (obj: T, key: U) : T[U]
// The second one takes string and will return any 
function getKey<T extends object> (obj: T, key: string) : any
// The implementation signature allows indexing, this will not be public.
function getKey(obj: { [index: string]: any }, key: string) {
    if (key in obj) {
        return obj[key] // Can now index T
    }
}

let r = getKey({ type: "test"}, "type"); // r is infered to `test`
let r2 = getKey({ type: "test"}, "notAProp"); // r is infered to any

Upvotes: 2

Related Questions