Skip to content
This repository was archived by the owner on Jan 14, 2021. It is now read-only.
This repository was archived by the owner on Jan 14, 2021. It is now read-only.

JSON type throws error on certain types of valid JSON on create and update #716

@thesunny

Description

@thesunny

Thank you @Jolg42 for addressing the previous two JSON issues:

This issue is the last (I think) for end-to-end JSON typing to work.

Bug description

We get a TypeScript error during a create or update for valid JSON in a JSON column if it contains optional properties like this.

type UserJSON = {
  name: string
  age?: number
}

The reason this happens is because TypeScript transforms the value of the age property by adding undefined so that it becomes number | undefined. The preceding type and the following type are identical:

type UserJSON = {
  name: string
  age?: number | undefined
}

This is a known limitation of TypeScript. It cannot distinguish a missing property from a property which can be set to undefined. microsoft/TypeScript#13195

How to reproduce

  • Create any prisma model with a JSON column
  • Do a create or update to the JSON column with a valid JSON object that has an optional property

This code clarifies why this happens:

// JSON values from `type-fest` as used in Prisma
export type JsonObject = {[Key in string]?: JsonValue};
export interface JsonArray extends Array<JsonValue> {}
export type JsonValue = string | number | boolean | null | JsonObject | JsonArray;

type User = {
  name: string,
  age?: number
}

type IsUserJSON = User extends JsonObject ? true : false
// false but should be true

Expected behavior

The way to fix it is to define a UJsonValue like this and use that to type JSON values in a create or update:

// JSON values with `undefined` allowed
export type UJsonObject = { [Key in string]?: UJsonValue }
export interface UJsonArray extends Array<UJsonValue> {}
export type UJsonValue =
  | undefined
  | string
  | number
  | boolean
  | null
  | UJsonObject
  | UJsonArray

type User = {
  name: string
  age?: number
}

type IsUserUJSON = User extends UJsonObject ? true : false
// true

The most accurate and strictest way to type this is to have the find methods return a JsonValue and the create and update methods to accept a UJsonValue.

This works because JSON data coming out of Prisma is guaranteed to be in the restrictive JsonValue type. It never contains undefined.

In an end to end API, that value comes out of the database will then be typed into something like User.

The end user makes a change and saves it to Prisma. The JSON data coming into Prisma should accept UJsonValue. If it is typed to accept JsonValue, we get a TypeScript error even though User is valid JSON.

The less accurate method is to add undefined to JsonValue and be done with it; however, this affects users that are strictly typing their API against JsonValue as typed in type-fest. It won't affect me and I think most users because if it flows through an API, that API has to accept undefined or else it breaks whenever there is an optional property (ie. same problem in a different place).

In order of desirability, it goes like this:

  • JsonValue for find and UJsonValue for create and update (Fixes all cases)
  • JsonValue extended with undefined (solves all my use cases and probably most people)
  • JsonValue as it is (breaks valid JSON with optional properties on create and update)

## Prisma information

Beta 2.0.0-beta7

Environment & setup

  • OS: Mac OS
  • Database: PostgreSQL
  • Prisma version: 2.0.0-beta7
  • Node.js version: 12.16.2

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions