'Is there some way to "spread" object properties to parameters of a function definition?
Best explained in code:
const ROUTE_PARAMS = {
userId: 'userId',
companyId: 'companyId',
};
// Is this somehow possible?
// `typeof` is obviously not the right tool here, just used as an example.
function buildRoute(typeof ROUTE_PARAMS): string;
// Result should be similar to:
function buildRoute2(userId: string, companyId: string): string;
// 🟩 The function should be called like this:
buildRoute('some-user-id', 'some-company-id');
// 🟥 I don't want the function to accept only 1 object like this:
buildRoute({ userId: 'some-user-id', companyId: 'some-company-id' });
The idea is to have the route params of buildRoute somehow dynamically "bound" to the object.
When defining the function params "manually" (like in buildRoute2), I always have to keep them in sync when changing properties of ROUTE_PARAMS...
Additional Info
I'll try to add some information as my question above does not seem to be sufficiently clear:
- I'm explicitly looking for a way to "type" this function from an object like the one shown above (
ROUTE_PARAMS). - I can't change this to some other structure (tuple type, some kind of array, ...) as that would require refactoring of other parts in my code which work with the object
ROUTE_PARAMS. - The function should except each object property as a single input parameter (i.e. function arity === number of object properties).
I don't want the function to accept all properties packed into 1 object (🟩 & 🟥 examples above).
Solution 1:[1]
At first, I thought what you want would be easily possible by using the spread operator with a tuple type. For instance,
function f(...args: [string, string]) {}
is (except of the parameter names) the same as
function f(arg0: string, arg1: string) {}
So we could try:
const ROUTE_PARAMS = {
userId: 'userId',
companyId: 'companyId',
};
function buildRoute(...args: typeof ROUTE_PARAMS[keyof typeof ROUTE_PARAMS][]): void {}
However, typeof ROUTE_PARAMS[keyof typeof ROUTE_PARAMS][] is an array, so that we could supply an arbitrary number of arguments to the resulting buildRoute function. It would be effectively the same as when you wrote function buildRoute(...args: string[]). Also, when the values are not only strings, but, for instance, a number was included, the inferred type would be (string | number)[] and we could supply a number at any position.
The difference to f above is that the type of the spread variable is a tuple for f but an array for buildRoute. So we would need to get a tuple out of the values of ROUTE_PARAMS. But, unfortunately, that is not supported and will not be, as a corresponding proposal was declined: https://github.com/microsoft/TypeScript/issues/13298.
So I now think that what you want is probably not possible with TypeScript.
Solution 2:[2]
Updated Answer:
As mentioned in Remirrors answer spreading function arguments to a specific set of properties and types on a variable is not supported. Remirrors answer explains well why this is the case.
However, just to extend on Remirrors answer to make the syntax a bit cleaner you can create a global type that uses a generic. This can then be reused in all the places you need without having to rewrite the long keyof code repeatedly:
// Re-useable type
export type TypeFromVar<T> = T[keyof T][];
const ROUTE_PARAMS = {
userId: 'userId',
companyId: 'companyId',
};
const User = {
name: 'Reece',
age: 25,
isVerified: true,
}
function buildRoute(...routes: TypeFromVar<typeof ROUTE_PARAMS>) {
const [userId, companyId] = args;
// do something
}
function otherFunction(...person: TypeFromVar<typeof User>) {
const [name, age, isVerified] = args;
// do something
}
buildRoute("userId1", "companyId2"); // only accepts string parameters
otherFunction("John", 36, false); // accepts string, number and boolean parameters
Original Answer:
Alternatively, if you can define array types for your variables you can get the exact number of arguments and their types by doing it like this:
type RouteParams = [
userId: string,
companyId: string,
]
function buildRoute(...routes: RouteParams): string {
const [userId, companyId] = routes;
// do something
}
buildRoute("user1", "company2")
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 | Remirror |
| Solution 2 |
