ThomasAribart / json-schema-to-ts

Infer TS types from JSON schemas 📝
MIT License
1.43k stars 30 forks source link

Omit doesn't work with the type returned by FromSchema #120

Closed spexii closed 1 year ago

spexii commented 1 year ago

If I'm just creating a type from schema with the following simple example, everything goes as expected; the type User has id and name.

const UserSchema = {
  type: 'object',
  properties: {
    id: { type: 'string' },
    name: { type: 'string' },
  },
  required: ['id', 'name'],
} as const

type User = FromSchema<typeof UserSchema>

However, if I create a new type of that type User using Omit, also the id property gets lost. And if I create a new object and type it with NewUser, TS is happy with the empty object.

type NewUser = Omit<User, 'name'>

Screenshot from 2023-02-03 12-35-07

I also tried to extend the type User, and it did the same regarding the original properties. The extended properties are in place.

type AnotherNewUser = Omit<User, 'name'> & {
  firstName: string
  lastName: string
}

const anotherNewUser: AnotherNewUser = {
  id: 'id',
  firstName: 'firstName',
  lastName: 'lastName',
  extra: 'random'
}

Screenshot from 2023-02-03 12-46-20

Am I missing something and/or is this intended?

em7v commented 1 year ago

Hi, additionalProperties: false, may help.

const UserSchema = {
  type: 'object',
  properties: {
    id: { type: 'string' },
    name: { type: 'string' },
  },
  additionalProperties: false,
  required: ['id', 'name'],
} as const

type User = FromSchema<typeof UserSchema>

type NewUser = Omit<User, 'name'>
ThomasAribart commented 1 year ago

Hi @spexii!

This is not intended but is not due to FromSchema but rather the way TS implements Omit as Pick<T, Exclude<keyof T, K>>;

Because you allowed additionalProperties (notice the [x:string]: unknown in User), keyof T is typed as string | number and so is the exclusion. Pick<User, string | number> will return the unexpected result.

My suggestions:

If none of those match your needs, you can always fallback on your properties keyword keys:

type NewUser = Pick<User, Exclude<keyof typeof UserSchema.properties, "name">>

Hope this solves your issue!

spexii commented 1 year ago

Thank you both! And thanks @ThomasAribart for the thorough explanation and different ways to solve this issue. :)