rostamiani
rostamiani

Reputation: 3245

Index signature for type 'string' is missing in type 'Object'.ts(2322)

I want to assign my custom object to JsonObject efied in Prisma package. this code generates error that I don't know why:

interface JsonArray extends Array<JsonValue> {}
type JsonValue = string | number | boolean | JsonObject | JsonArray | null
type JsonObject = {[Key in string]?: JsonValue}
// ^ These are defined in Prisma package and I cannot change them

interface Object {
  name: string
}

let object : Object
let jsonObject : JsonObject 

// This line has error
jsonObject = object;

// Type 'Object' is not assignable to type 'JsonObject'.
// The 'Object' type is assignable to very few other types.
// Did you mean to use the 'any' type instead?
// Index signature for type 'string' is missing in type 'Object'.ts(2322)

Upvotes: 3

Views: 6621

Answers (3)

Denis P
Denis P

Reputation: 796

I think the previous answers don't give a solution to this common problem. Prisma describes a very generic type for JSON objects (with index signature), the specific types we use in real life are pretty much never generic enough (think of percentage of your real life types where you use index signatures).

My solution is just to use typecast: myObject as object. It then gets compatible with Prisma.JsonObject.

I'm sure my object is convertible to JSON (e.g. I know it was received from external API via JSON and then transformed/validated - what could go wrong here?), I just want to save it without bloating the code, so I'm pretty sure this is a good way to handle this situation.

I admit there may be some rare cases when this won't be a good solution (because a JS object fields are not convertible to JSON, such as in objects with circular references), but frankly I couldn't imagine a realistic case when you create a field with type Json in Prisma and then use an object that just can't be converted to JSON.

Upvotes: 0

First of all, don't name your custom object type Object. Type Object is built in type for Object value. It may confuse people. I'm not sure whether you want to update existing Object type or you want just to create some custom type, it is important for interfaces because of declaration merging.

consider this example:

interface JsonArray extends Array<JsonValue> { }

type JsonValue = string | number | boolean | JsonObject | JsonArray | null

type JsonObject = { [Key in string]?: JsonValue }

interface CustomObject {
    name: string
}

declare let object: CustomObject
declare let jsonObject: JsonObject

// This line has error
jsonObject = object;

The error you have means that jsonObject is indexed. In other words it means that you can use any string to access the object values. For instance:

jsonObject['hello'] // ok

Whereas it is not true for object. In object case you are allowed to use only name key to access appropriate value. You are not allowed to use hello.

object['hello'] // error

Now, imagine the situation where TS allows you to do this operation:

jsonObject = object;

jsonObject['hello'] // undefined, because  it has only `name` property

SO if you want to make it assignable you should add indexing to your CustomObject:

interface CustomObject {
    [prop: string]: JsonValue
    name: number
}

Or, what is more interesting use type keyword for declaring CustomObject instead of interface:

interface JsonArray extends Array<JsonValue> { }

type JsonValue = string | number | boolean | JsonObject | JsonArray | null

type JsonObject = { [Key in string]?: JsonValue }

type CustomObject= {
    name: number
}

declare let object: CustomObject
declare let jsonObject: JsonObject

jsonObject = object; // no error

jsonObject.name

Yes, there is a difference in terms of indexing between interface and type. See my answer and article

Please keep in mind that this operation is allowed:

jsonObject['hello'] // undefined but allowed

There is no compiler error but it is unsafe.

Upvotes: 4

Szaman
Szaman

Reputation: 2388

There are a few issues here.

  • Object is a built-in javascript data type. You should name your interface something else, e.g. MyObject,
  • You are declaring a variable object but it is not initialised, i.e. it doesn't have any value. You cannot use it in operation like x = object,
  • Your interface does not fully overlap with JsonObject. You can either modify the interface or use type.
interface JsonArray extends Array<JsonValue> {}
type JsonValue = string | number | boolean | JsonObject | JsonArray | null
type JsonObject = {[Key in string]?: JsonValue}
// ^ These are defined in Prisma package and I cannot change them

type MyObject = {
  name: string
}

let anObject: MyObject = {name: "foo"}
let jsonObject: JsonObject 

// no error
jsonObject = anObject

Try it in the typescript playground.

Upvotes: 2

Related Questions