Home > database >  How do I omit a key in type with dynamic field
How do I omit a key in type with dynamic field

Time:09-30

How do I define UserValue type that omit id field from User

interface User {
  [x: string]: any
  avatarUrl?: string
  bio?: string
  company?: string
  email?: string
  emailVerified?: boolean
  firstName?: string
  id: types.TimeUuid
}
type UserValue = Omit<User, 'id'>

Result of current solution will be

type UserValue = {
    [x: string]: any;
    [x: number]: any;
}

CodePudding user response:

In your case You could try to make all fields optional. That will allow you to create object without id field, but Pawel's suggestion in comments is better.

type AllOptional<Type> = {
  [Property in keyof Type]?: Type[Property];
};


interface User {
  [x: string]: any
  avatarUrl?: string
  bio?: string
  company?: string
  email?: string
  emailVerified?: boolean
  firstName?: string
  id: number
}

type UserValue = AllOptional<User>

const x: UserValue = {
  email: "xxx",
}

Playground Link

CodePudding user response:

type S = 'foo' | string will resolve to type S = string.

And that's the root of your problem. By including [x: string], you make it impossible to Omit any properties from User. To understand why, take a look at the definition for Omit:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
type Exclude<T, U> = T extends U ? never : T;
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Translate this to your User interface:

// Simplified User:
interface User {
  [x: string]: any
  etc?: string
  id: number
}

// This
type UserValue = Omit<User, 'id'>;
// is equivalent to
type UserValue = Pick<User, Exclude<keyof User, 'id'>>;

Because [x: string] allows for any string (or number, but not Symbol) property accessor, keyof User allows for any string (or number) value. Excluding the type 'id' from string is still string.

type NotId = Exclude<string, 'id'>
//   ^^^^^ type NotId = string

So what you end up with is a type that picks all string accessor properties from interface User giving you

type UserValue = {
    [x: string]: any;
    [x: number]: any;
};

So Omit will not work on User, at least not the way you want.


Solution

If you have been using the User type in your code, you don't want to change the definition. Instead, I recommend creating a base interface that does not include [x: string]: any for User to extend:

interface UserBase {
  avatarUrl?: string
  // ...
  id: types.TimeUuid
}

interface User extends UserBase {
  [x: string]: any
}

type UserValue = Omit<UserModel, 'id'>

Or, don't use Omit at all:

interface UserValue {
  [x: string]: any
  avatarUrl?: string
  // ...
  firstName?: string
}

interface User extends UserValue {
  id: types.TimeUuid
}

Here is a playground exploring these concepts and the solution.

  • Related