enum Friend {
JOHN,
SALLY,
PAUL,
}
// solution at type level
// type constructor
type MyFriends<F extends Friend[]> = {
friends: F,
bestFriend: F[number]
}
// make specific instance of it:
type JohnFriends = MyFriends<[Friend.SALLY,Friend.PAUL]>
const johnFriends: JohnFriends = {
friends: [Friend.SALLY,Friend.PAUL],
bestFriend: Friend.SALLY // can be only a member of friends array
}
const errorJohnFriends: JohnFriends = {
friends: [Friend.SALLY,Friend.PAUL],
bestFriend: Friend.JOHN // error as it should!
}
// solution at value level with value constructor
// additionaly we can make value constructor for proper creating such type of value
const makeFriends = <A extends Friend[], BF extends A[number]>(friends: A, bestFriend: BF): MyFriends<A> => ({
friends,
bestFriend
})
// propelry created value of MyFriends
const johnFriends2 = makeFriends([Friend.PAUL, Friend.JOHN], Friend.JOHN);
const errorJohnFriends2 = makeFriends([Friend.PAUL, Friend.JOHN], Friend.SALLY); // error as it should
То, что мы сделали здесь:
MyFriends
generi c тип с аргументом Friend
, это означает, что мы передаем, какие именно элементы из Friends
enum будут быть доступным в friends
свойство bestFriend: F[number]
- мы говорим, что второе поле может быть только значением, которое существует в массиве friends
, благодаря чему мы не можем использовать элемент Enum, который не предусмотрен в первом