0

How do I let TypeScript know that a function validates that an item contains a given key?

e.g.:

function doesItemHaveKey(item: any, key: string): boolean {
  return typeof item === 'object' && item !== null && typeof item[key] !== 'undefined';
}

interface testInterface {
  optional?: string;
  some: string;
}

let testObj: testInterface = {
  some: 'value'
};

if (doesItemHaveKey(testObj, 'some')) {
  // Do something with testObj.some
  // TypeScript throws errors because `testObj.some` could be undefined
}

Things I've tried:

if (doesItemHaveKey(testObj, 'some') && typeof testObj.some !== 'undefined') {
  // This works, but duplicates the typeof check
}

function doesItemHaveKey(item: any, key: string): key is keyof item
/**
 * A type predicate's type must be assignable to its parameter's type.
 *  Type 'string | number | symbol' is not assignable to type 'string'.
 *    Type 'number' is not assignable to type 'string'.
 **/
2
  • I think you forgot an ? on some otherwise some is not types as possibly undefined Commented Feb 5, 2020 at 21:12
  • Even if it is optional, testObj.some doesn't give a type error in that code. Commented Feb 5, 2020 at 21:14

2 Answers 2

1

The type assertion version will work, you just need to add some generic type parameters to capture the item type (in T) and the actual type (in K). You can then use the type-guard syntax to tell the compiler the item parameter is T in an intersection where the key K is required (which will effectively make the key K required)

function doesItemHaveKey<T, K extends keyof T>(item: T, key: K): item is T & Required<Pick<T, K>> {
  return typeof item === 'object' && item !== null && typeof item[key] !== 'undefined';
}

interface testInterface {
  optional?: string;
  some?: string;
}

let testObj: testInterface = {
  some: 'value'
};

if (doesItemHaveKey(testObj, 'some')) {
  testObj.some.big() // string 
}

Playground Link

Sign up to request clarification or add additional context in comments.

Comments

1

You can make doesItemHaveKey a user-defined type guard, using generic type parameters and Exclude to construct a subtype of T where the key K's value is not undefined.

type NotUndefined<T, K extends keyof T> = T & Record<K, Exclude<T[K], undefined>>;

function doesItemHaveKey<T, K extends keyof T>(item: T, key: K): item is NotUndefined<T, K> {
    return typeof item === 'object' && item !== null && typeof item[key] !== 'undefined';
}

Playground Link

1 Comment

unlike the accepted answer this one worked for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.