Typescript ERROR: Index signature is missing in type [SOME_OBJECT]

Viewed 642

I created a function that receives a string keys based object with any desired value.

The function definition line:

export const pickFromObj = <
  Obj extends { [K: string]: unknown }, 
  PickKeys extends keyof Obj
>(object: Obj, keys: PickKeys[]) => {
  1. First I would like to know if there is any way to make this shorter.

  2. In addition, when I pass some the following object to the function, I receive the following error from TS compiler:

Argument of type 'IExample' is not assignable to parameter of type '{ [x: string]: unknown; }'.
  Index signature is missing in type 'IExample'.ts(2345)

IExample:

export interface IExample {
  fromAddress: string;
  toAddress: string;
  status: number;
}
3 Answers

This is because { [key: string]: unknown } means that the object has an index signature. So you can use any string as a key for this object, while IAreasInfoFormFields does not have index signature.

Consider this example:

type Indexed = { [K: string]: unknown }

interface NotIndexed {
  name: string;
}

const foo = <T extends Indexed>(obj: T) => obj

const obj: NotIndexed = {
  name: 'name',
}

foo(obj) // error

Here you see an error because you are not allowed to do smth like this:

interface NotIndexed {
  name: string;
}

const obj: NotIndexed = {
  name: 'name',
}

obj.hello // error

But if you have indexed object, you are allowed to use any string key:

type Indexed = { [K: string]: unknown }

const obj: Indexed = {
  name: 'name',
}

obj.hello // ok

One interesting thing, that types are indexed by the default. If you will use type keyword instead interface keyword, this code will compile:

type Indexed = { [K: string]: unknown }

type NotIndexed= {
  name: string;
}

const foo = <T extends Indexed>(obj: T) => obj

const obj: NotIndexed= {
  name: 'name',
}

foo(obj) // ok

This is the difference between interfaces and types. See related answer.

So, if you use type keyword instead of interface in Example interface, it should compile

  1. Oh yes there is:
const pickFromObj = <O, K extends keyof O>(object: O, keys: K[]) => { };
  1. The code above also gets rid of your indexing error :)

Link to TS Playground here

come if you wanna see the light. do not complicate yourself.

basically


interface Person {
    /** the first name of the person */
    first: string;
    /** the first name of the person */
    last: string;
    /** contact email of the person */
    email: string;
    /** person's age */
    age:number;
}

/**
 * @description Get the value of a property from a given obj.
 *
 * @date 06/07/2021
 * @export
 * @template Obj type definition of the obj
 * @param {keyof Obj} key a property inside of our obj.
 * @param {Obj} obj any object from where we want the value.
 * @returns {Obj[key]} the value of the prop.
 * @example
 * const last = getFromObj<Person>("last", eneto);
 */
export function getFromObj<Obj>(key: keyof Obj, obj:Obj):Obj[keyof Obj] {
    return obj[key];
}

const eneto: Person ={

    first:"Ernesto",

    last:"Jara",

    email:"email@email.com",
    age:19,
}

const name = getFromObj<Person>("first", eneto);
const age = getFromObj<Person>("age", eneto);

console.log("name", name);
console.log("age", age);

There you have it, if you hover over name or age vscode or yow favorite editor will say

const name: string | number;

but you know name is an string and age is a number, so you need to let TS to know that as well.

Do it like this



const name = getFromObj<Person>("first", eneto) as string;
const age = getFromObj<Person>("age", eneto) as number;


now if you hover over name it'll show you

const name: string;
Related