As listed below, let's say we have:
- a function
r
that combines multiple tasks together - and a function
o
that returns a shape likewhen(cb).map(cb)
every callback passed to when
or map
should always take these 3
arguments: S, A, C
where S
and C
are defined in r
and A
is defined in o
.
here is a link to the typescript playground which also shows the error I am getting.
My question is: How can I get a typesafe declaration?
type Task<S, A, C> = <AA extends A>(s: S, a: AA, c: C) => any;
type Fn<S, A, C> = (s: S, a: A, c: C) => any;
const r = <S, C>() => ({
tasks: (...tasks: Task<S, any, C>[]) => null,
});
const o = <T, A = { prop: T }>(type: T) => ({
when: <S, C>(fp: Fn<S, A, C>) => ({
map: <SS extends S, CC extends C>(fn: Fn<SS, A, CC>): Task<SS, A, CC> => (
(s: SS, a: A, c: CC): any => (
fp(s, a, c) ? fn(s, a, c) : s
)
),
}),
});
const result = r<2, 7>().tasks(
o(44) // expect: cb(2, 44, 7)
.when((s, a, c) => s + a.prop + c)
.map((s, a, c) => s + a.prop + c),
o(78) // expect: cb(2, 78, 7)
.when((s, a, c) => s + a.prop + c)
.map((s, a, c) => s + a.prop + c),
// etc...
// callback provided to `.map` is typesafe,
// callback provided to `.when` is not,
);
As you can see, the callback provided to when
is not typesafe: Params S
and C
are lost.
Hmm, among other problems, it looks like you want some contextual type inference that the language doesn't provide for. Here is how I would recommend typing things:
You've changed your definitions since I worked on this, so the following might not match up exactly with what you posted. In short:
Make
r().tasks()
take an array (possibly a tuple) ofFn<S, any, C>
values, so the fact that the second task'sA
is not the same as the first won't cause an error.Don't have a generic
T
and also a genericA = {prop: T}
. I'm guessingA
is not meant to be independent fromT
and you are trying to use a default type parameter to represent some kind of assignment, but it doesn't really work that way. Instead, just useT
and then replace all instances ofA
with{prop: T}
.Only have as many generic types as you need and as close to the desired inference place as possible. I've moved
S
andC
too().when
.Finally, contextual typing from the parameter of
r<2.7>().tasks()
to the values ofS
andC
ino().when()
does not occur. The compiler probably doesn't even try to do it, since the inference would have to happen across multiple levels of function call. The only way to deal with that seems to be to re-specifyS
andC
, either by annotating thes
andc
parameters of the callback passed too().when()
, or by callingo().when<2,7>()
.Hope that helps point you in the right direction. Good luck!