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",
}
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.
