'how to have type dependent on additional type
Consider I have names a object storing some object used as interface
const items = [{name: "drop", value:"1"}, {name: "push", value:"2"}, {name: "increment", value:3}];
I also have the given interface
interface SpecialAbilities {
bob: { drop(name: string): void };
damian: { push(name: string): void};
tubby: { increment(index: number): void};
}
I have foo = (key, array) which looks up keys by array.
I want foo to use foo in a way that it autocompletes the key argument:
foo<SpecialAbilities["bob"]>("drop",items) // autosuggests "drop" in vscode :)
foo<SpecialAbilities["bob"]>("increment",items) // TypeError
How would I do that?
Solution 1:[1]
First, in order to preserve type information about the string literal types of the name properties of the items elements, you need to change how it's declared. Right now the name property is just string, and the compiler completely forgets about specific strings like "drop" and "push":
const items = [{ name: "drop", value: "1" }, { name: "push", value: "2" }, { name: "increment", value: 3 }];
/* const items: ({
name: string;
value: string;
} | {
name: string;
value: number;
})[] */
A const assertion is the easiest way to do this:
const items = [
{ name: "drop", value: "1" },
{ name: "push", value: "2" },
{ name: "increment", value: 3 }
] as const;
/* const items: readonly [{
readonly name: "drop";
readonly value: "1";
}, {
readonly name: "push";
readonly value: "2";
}, {
readonly name: "increment";
readonly value: 3;
}] */
That's better.
From your requirement that you want the following behavior:
foo<SpecialAbilities["bob"]>("drop", items) //autosuggests "drop" in vscode :)
foo<SpecialAbilities["bob"]>("increment", items) //typeError
I suppose that you want foo() to be a generic function, where the generic type parameter corresponds to some type whose property keys should be acceptable for the first function argument. Everything else is not 100% clear to me, so I will just do the easiest thing from my perspective: the second function argument can be of the any type. Here's how it looks:
declare function foo<T>(name: keyof T, items: any): void;
Let's test it:
foo<SpecialAbilities["bob"]>("drop", items) // okay
foo<SpecialAbilities["bob"]>("increment", items) // error!
// ------------------------> ~~~~~~~~~~~
// Argument of type '"increment"' is not assignable to parameter of type '"drop"'.
// foo<SpecialAbilities["tubby"]>( | );
// ^-- ? "increment"
You get the behavior you asked for, including the IntelliSense suggestions.
So that answers the question as asked. I'm a bit wary that you actually want to use it like this, though. Seems like you probably want something more specific than any for the type of items. But that's out of scope here, so I will leave it alone.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jcalz |
