Reputation: 629
I have a following example functions (compose is from Ramda):
declare function compose<V0, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0) => T1): (x0: V0) => T2;
interface User {
id: number
}
function fn1(input: any): User {
return {id: 1};
}
function fn2<I, O>(fn: (i: I) => O): (i: I) => O {
return (data) => {
try {
return fn(data);
} catch (e) {
return undefined
}
};
}
When I tried to use it like this:
compose(fn2(user => user.id), fn1);
TypeScript throws a following error:
TS2339:Property 'id' does not exist on type '{}'.
Does anyone know, what should I do to help TypeScript to infer a proper type for user
?
Of course, following code will work:
compose<any, User, number>(fn2(user => user.id), fn1);
Upvotes: 2
Views: 189
Reputation: 7354
It looks like that parametric types are resolved left to right and because you are writing an any to any function first you have that error. Look at this code which is similar but the functions are reversed:
declare function compose<V0, T1, T2>(fn0: (x0: V0) => T1, fn1: (x: T1) => T2,): (x0: V0) => T2;
interface User {
id: number
}
function fn1(input: number): User {
return {id: 1};
}
function fn2<I, O>(fn: (i: I) => O): (i: I) => O {
return (data) => {
try {
return fn(data);
} catch (e) {
return undefined
}
};
}
compose(fn1, fn2(user => user.id));
It works as you would expect. but the compose definition must be changed.
Similar to Titian Cernicova's answer, you can define at least the first parametric element that goes into your compose chain like this:
declare function compose<T1 = any, T2 = any, V0 = any>(fn1: (x: T1) => T2, fn0: (x0: V0) => T1): (x0: V0) => T2;
interface User {
id: number
}
function fn1(input: number): User {
return {id: 1};
}
function fn2<I, O>(fn: (i: I) => O): (i: I) => O {
return (data) => {
try {
return fn(data);
} catch (e) {
return undefined
}
};
}
compose<User>(fn2(user => user.id), fn1);
Upvotes: 1
Reputation: 250106
You don't have to specify all the types but you need to specify the user
parameter type for this to work.
compose(fn2((user: User) => user.id), fn1);
If you were to not use fn2
type inference would work, but deciding that fn2
's argument has to be of type (user:User)=> number
based on the required value to be returned by fn2 is a bit too much for the Typescript compiler.
This works:
compose((user) => user.id, fn1);
Upvotes: 0