I've written an update function that takes an object, T
, and an "updater" object, which takes the same keys (or a subset) as T
and provides a function for updating the corresponding value of the original object.
type UpdaterObj<T> = {
[K in keyof Partial<T>]: (t: T[K]) => T[K]
};
declare function update<T>(obj: T, updaterObj: UpdaterObj<T>): T
Sample usage is like this:
const foo = {
x: 1,
y: "1"
};
update(foo, {
x: (xx: number) => xx + xx,
y: (yy: string) => yy + yy,
}); // Result is { x: 2, y: "11" }
This works, but I'd really like to remove those annotations and just write x: (xx) => xx + xx
and y: (yy) => yy + yy
, however I get the Parameter implicitly has type 'any'
error, for both xx
and yy
.
It's odd because if I mouse over x:
, it correctly infers what the corresponding value should but, and it complains if I annotate xx
or yy
as the wrong type, and I'm pretty sure I've seen typescript make similar inferences before.
The annotations are pretty simple in this case, but can get annoying if I'm deep within objects or with more complex types, so inference would be really helpful here.
Is there any simple tweak to my definitions or my usage that can get typescript to infer this for me?
I'm not sure if there's an existing issue in GitHub about how type inference of function parameters interacts with inference for generic type parameters, so I don't know if there's a simple tweak of the function signature which makes it work without affecting the call site.
Looking at the TypeScript spec , I see that for TypeScript function expressions (like xx => xx + xx
),
When a function expression with no type parameters and no parameter type annotations is contextually typed by a type
T
and a contextual signatureS
can be extracted fromT
, the function expression is processed as if it had explicitly specified parameter type annotations as they exist inS
.[snip]
A contextual signature
S
is extracted from a function typeT
as follows:
If
T
is a function type with exactly one call signature, and if that call signature is non-generic ,S
is that signature.If
T
is a union type... [snip]Otherwise, no contextual signature can be extracted from
T
.
So it kind of looks like since ( xx => xx + xx
) in your original call has a function type of the form (t: T['x']) => T['x']
that includes the type parameter T
, it doesn't process the function as if it had explicit type parameters. Presumably that means the compiler tries to extract a contextual signature before it eliminates the generic parameter.
If it's okay to change the call site, then the simplest suggestion I can make which demonstrably works is to break the two-argument update()
function into a curried function like so:
declare function updateCurried<T>(obj: T): (updaterObj: UpdaterObj<T>) => T
which would be used as follows (note the sequence of function calls):
updateCurried(foo)({
x: xx => xx + xx,
y: yy => yy + yy,
}); // okay
This essentially forces the compiler to evaluate and eliminate the generic type parameter by the time it gets to evaluating the updaterObj
prarameter, at which point it can do the contextual function parameter inference as described in the above excerpt from the spec.
Hope that's helpful to you. Good luck!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.