Reputation: 5925
I have a function that is called multiple times.
function foo(parameter) {
parameter.a;
parameter.b;
parameter.c;
parameter.d;
}
The arguments are different for each call, but they're all literals and have the same structure.
const first = { a: 1, b: 2, c: 3, c: 4};
const second = { a: 2, b: 4, c: 6, c: 8};
foo(first);
foo(second);
Also, those objects have an awful lot of attributes and because of that it's tedious to manually specify the type of the parameter, which leads to my question: how to infer the type of foo
's parameter? The compiler correctly infers the types of first
and second
, but doesn't seem to be able to infer the type of parameter
.
Here's what I've tried.
You can put an instance into the type position.
foo(parameter: typeof first) {
parameter.a;
parameter.b;
//...
}
It works and the type is correct, but I'd like to avoid using this approach because the function and the literals are defined in different files, and I don't want to import a literal into the file with function definition.
If I convert the function to a lambda, the compiler seems to happily infer the type. It would look something like this:
function wrapper<T>(parameter: T, foo: (p: T) => void) {
foo(parameter);
}
wrapper(first, parameter => { // parameter is inferred to be `typeof first`.
parameter.a;
parameter.b;
//...
})
But as soon as I extract the labmda into a variable (which I need to reuse the function), it's inferred to any.
function wrapper<T>(parameter: T, foo: (p: T) => void) {
foo(parameter);
}
const lambda = parameter => { // parameter is `any`
parameter.a;
parameter.b;
//...
}
wrapper(first, lambda)
Upvotes: 1
Views: 1147
Reputation: 10377
I think interfaces are your only option here. You could declare a dummy object that has the properties you're interested in, but at the end of the day that's equivalent.
When you create those primitive objects Typescript is constructing an implicit interface with those properties for that variable.
the function foo
needs the type information it's expected to work on at the time of its definition to be able to adequately typecheck.
Your approaches:
This works because typeof first is giving the parameter the implicitly constructed interface
{
a:number;
b:number;
c:number;
d:number;
}
and so those fields are available in the function.
This works for the same reason, the overall wrapper function is specializing on the type of the first parameter (in this case the first
variable) which is that same implicit interface. The parameter in this case is understood to be of that same type and so the properties a,b,c,d are available.
The lambda is interpreted in the exact same way that your original function definition was interpreted, with no type information on the parameter. While that lambda is used in the wrapper function in your code, there is no guarantee that lambda will only be used in such a manner, so it is unsafe to project the type information. This is also the same reason your function fails.
Now there are some languages that can handle this. For example, I think OCaml can intelligently implicitly determine the type of a record variable based on the fields you're using and what's in scope. Typescript, however, appears not to. You'll have to tell it what you're expecting. You could use inline interfaces if you wish, but given how long you describe your interfaces as being I doubt that's worthwhile.
tl;dr: Use explicit interfaces, they're good for you.
Upvotes: 1
Reputation: 14447
How about an interface?
interface ILargeObject {
a: number;
b: number;
c: number;
d: number;
}
const first: ILargeObject = { a: 1, b: 2, c: 3, d: 4 };
const second: ILargeObject = { a: 1, b: 2, c: 3, d: 4 };
function foo(parameter: ILargeObject) {
/* ... */
}
You can then import the interface into both files. This addresses your concern about having to import a literal into the file with the function definition.
An important benefit is that your object type will be defined in one place instead of having the function parameter type relying on a single object definition. If that individual object is changed some point in the future it will break your function along with it.
This method provides more security and it's logical and clean.
Upvotes: 0