Consider the following type:
type UserList = {
userIds: string[]
userData: {
[UserId in UserList['userIds'][number]]: {
userId: UserId
username: string
}
}
}
with that I want to tell TypeScript compiler two things:
userData
keys must exist inuserIds
userId
field must be equal to the key of the same entry
I feel that I need some kind of this
or self
keyword instead of UserList
CodePudding user response:
I don't believe it's possible to refer to the exact string values inside userIds
without resorting to using a generic type parameter:
type UserList<T extends ReadonlyArray<string>> = {
userIds: T
userData: {
[UserId in T[number]]: {
userId: UserId
username: string
}
}
}
And your type definition would have to look as follows:
const a: UserList<['hello']> = {
userIds: ['hello'],
userData: {
hello: {
userId: 'hello',
username: 'username'
}
}
}
If you don't want to specify the user IDs twice (inside the generic parameter and inside the actual UserList
variable), you have to use a wrapper function:
function asUserList<T extends ReadonlyArray<string>>(list: UserList<T>) {
return list;
}
const a = asUserList({
userIds: ['hello'] as const,
userData: {
hello: {
userId: 'hello',
username: 'username'
}
}
})
If you don't use a generic parameter and instead try to use the this
type inside an interface like this:
interface UserList {
userIds: string[]
userData: {
[UserId in this['userIds'][number]]: {
userId: UserId
username: string
}
}
}
It wouldn't work because this['userIds']
would always resolve to the weak string[]
type, not a specific set of strings that would allow you to strongly type userData
based on the exact value of userIds
.