Reputation: 94
How can I write an interface where one of the fields is a member of an array type in that same interface?
So far, I've got this:
interface Example<F extends string = string> {
fruits: ReadonlyArray<F>;
defaultFruit: F;
}
// or equivalently:
interface Example<F extends ReadonlyArray<string> = ReadonlyArray<string>> {
fruits: F;
defaultFruit: F[number];
}
But the problem with these is that they will allow things like:
const e: Example = {
fruits: ["Apple", "Banana"],
defaultFruit: "Something Else",
}
as F will extend itself to "Apple" | "Banana" | "Something Else"
.
I can do this explicitly, with something like
interface Example<F extends string> {
fruits: ReadonlyArray<F>;
defaultFruit: F;
}
const e: Example<"Apple" | "Banana"> = {
fruits: ["Apple", "Banana"],
defaultFruit: "Something Else", // <- correctly complains that this is not assignable to F
}
But I'd rather not write everything out twice. I'd like to be able to have:
const e: SomeInterface = {
fruits: ["Apple", "Banana"],
defaultFruit: "Something Else", // <- that disallows this.
}
Is there a way to automatically derive this? Thanks.
Upvotes: 1
Views: 35
Reputation: 276165
as F will extend itself to "Apple" | "Banana" | "Something Else".
To prevent this, you should create a separate generate constrained by F.
interface Example<F extends string, D extends F> {
fruits: ReadonlyArray<F>;
defaultFruit: D;
}
function check<F extends string, D extends F>(value: Example<F, D>){return value}
const e = check({
fruits: ["Apple", "Banana"],
defaultFruit: "Something Else", // Error Type '"Something Else"' is not assignable to type '"Apple" | "Banana"'
});
Upvotes: 1