Reputation: 4453
I have a function that takes a dictionary and an array of strings.
f({ a: "foo", b: "bar" }, ["a", "b"]);
I want to enforce that the values in the array are the keys of the dictionary. I.e, the call above is legitimate, but the two calls below must fail at compile time. The first one because it has an extra key (in the dictionary), the second one because it has an extra value (in the array).
f({ a: "foo", b: "bar", c: "baz" }, ["a", "b"]);
f({ a: "foo", b: "bar" }, ["a", "b", "c"]);
I don't know if it helps, but I was able to write a test that checks that the keys and the values are exactly the same.
type Valid<
D extends Record<string, string>,
A extends Array<string>
> = A[number] extends keyof D ? keyof D extends A[number] ? any : never : never;
This seems to work; Valid<D, A>
is any
when the keys and the values are the same, and is never
when there is an extra key or an extra value.
How can I use Valid<D, A>
to make the compilation fail? I tried stuff like this:
function f<
D extends Record<string, string>,
A extends Array<string> & Valid<D, A>
>(d: D, a: A): void
{
console.log(d, a);
}
but so far nothing has worked. The definition above for instance causes a compile time error even when the keys and values match, which is of course not what I want.
Thanks to @davidhu I have half of what I need; @davidhu suggested this, which errors in one of the two cases.
function f<
D extends Record<string, string>,
A extends Array<keyof D>
>(d: D, a: A): void
{
console.log(d, a);
}
// Errors because of the "c" in the array.
f({ a: "foo", b: "bar" }, ["a", "b", "c"]);
To cover the other half of it, I came up with this.
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
function f<A extends readonly string[]>(d: UnionToIntersection<{ [K in A[number]]: string }>, a: A): void
{
console.log(d, a);
}
// Errors because of the `c: "baz"` in the dictionary.
f({ a: "foo", b: "bar", c: "baz" }, ["a", "b"] as const);
Hopefully there is a way to combine the two things together.
Upvotes: 0
Views: 40
Reputation: 10472
function f<
D extends Record<string, string>,
A extends Array<keyof D>
>(d: D, a: A): void
{
console.log(d, a);
}
f({ a: "foo", b: "bar", c: "baz" }, ["a", "b"]);
f({ a: "foo", b: "bar" }, ["a", "b", "c"]);
You just need to say A
is an array of keys
of D
and it shows a compile error
Upvotes: 1