enum Friend {
JOHN,
SALLY,
PAUL,
}
// implementing at the type level
// defining a type constructor
type MyCircle<C extends Friend[]> = {
friends: C,
bestFriend: C[number]
}
// creating a specific instance:
type JohnCircle = MyCircle<[Friend.SALLY,Friend.PAUL]>
const johnCircle: JohnCircle = {
friends: [Friend.SALLY,Friend.PAUL],
bestFriend: Friend.SALLY // must be a member of friends array
}
const errorJohnCircle: JohnCircle = {
friends: [Friend.SALLY,Friend.PAUL],
bestFriend: Friend.JOHN // should result in an error!
}
// solution at the value level with a value constructor
// we can also define a function for creating values of this type
const makeCircle = <X extends Friend[], BF extends X[number]>(friends: X, bestFriend: BF): MyCircle<X> => ({
friends,
bestFriend
})
// properly created value of MyCircle
const johnCircle2 = makeCircle([Friend.PAUL, Friend.JOHN], Friend.JOHN);
const errorJohnCircle2 = makeCircle([Friend.PAUL, Friend.JOHN], Friend.SALLY); // should result in an error
In summary:
MyCircle
is a generic type that takes elements from the Friend
enum, specifying which ones will be included in the friends
property
bestFriend: C[number]
- ensures that the second field can only be a value present in the friends
array, preventing the use of Enum members not provided in the initial list