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.