Reputation: 10620
please see code sample below- i'd like some typesafety around the type of array entries that resetHistory
takes in, but it's not creating a type error where i'd like it to! appreciate any tips.
type Params = {
profile: { userId: string };
homepage: undefined;
settings: { tab: "password" | "profile" };
};
// naive attempt at typing the history arg
function resetHistory<K extends keyof Params>(
history: { name: K; params: Params[K] }[]
) {
// ...
}
resetHistory([
{ name: "homepage", params: undefined },
{ name: "profile", params: { userId: "1234" } },
{ name: "settings", params: { userId: "xxx" } }, // <-- i want this to be a typeerror, and only accept { tab: "password" | "profile" }
]);
Upvotes: 1
Views: 39
Reputation: 42198
What you need here is a union of all possible pairings. We can create that with a mapped type.
type HistoryEntry = {
[K in keyof Params]: Params[K] extends undefined
? { name: K; params?: undefined; }
: { name: K; params: Params[K]; }
}[keyof Params]
For each key of the Params
object, the acceptable value is an object with a name
that matches that key and a params
that matches the Params
value for that key. That is { name: K; params: Params[K]; }
.
I am adding a conditional check Params[K] extends undefined
to allow you to omit the params
entirely if they are undefined
. So you can just pass { name: "homepage" }
.
Our resetHistory
function is no longer generic. It accepts an array where all entries are of type HistoryEntry
.
function resetHistory( history: HistoryEntry[] ) {
This gives us all of the errors that we want:
resetHistory([
{ name: "homepage" }, // ok
{ name: "profile", params: { userId: "1234" } }, // ok
{ name: "settings" }, // error: params is missing
{ name: "settings", params: { userId: "xxx" } }, // error: tab is missing from params
{ name: "settings", params: { tab: "password" } } // ok
]);
Upvotes: 3