问题
lets imagine type T:
type T = {
prop1: (s: S) => T1,
prop2: (s: S) => T2,
prop3: (s: S) => T3,
}
and now lets imagine type W
type W = (s: S) => {
prop1: T1,
prop2: T2,
prop3: T3,
}
its easy to write a function that maps T to W by hand,
is it possible to write generic type sefe version of it in typescript?
function x(t: T): W {
return funtion(s: S) {
prop1: t.prop1(s),
prop2: t.prop2(s)
prop3: t.prop3(s)
}
}
what kind of feature language is missing to facilitate this, something like higher order generic types?
回答1:
You can indeed write a generic version of this in TypeScript:
function x<S, V>(t: {[K in keyof V]: (s: S) => V[K]}): (s: S) => V {
return function(s: S): V {
const ret = {} as V;
Object.keys(t).forEach((k: keyof V) => {
ret[k] = t[k](s);
})
return ret;
}
}
const xSpecific: (t: T) => W = x; // okay
Note that V is the return type of your W function. (So W is essentially the same as (s: S) => V.) And the input to x is a mapped type corresponding to T: it has the same keys as V, but its values are functions from S to the corresponding properties of V.
You can get away with having the function input being a mapped type and the output being an unmapped one because TypeScript supports inference from mapped types. Otherwise you'd need something like the proposed "extended typeof" feature to derive W from T generically. (This might be the missing language feature to which you're alluding.)
As for the implementation, I'm looping over the keys of t and applying each function t[k] to the input s.
And xSpecific is the same as x narrowed to the particular T and W types you posted. This compiles because TypeScript recognizes that the generic x is compatible.
Now for the caveats and fine print. Unfortunately, the compiler isn't able to reliably infer the type of S from x directly. If you just call the generic x() with a T input, you get this:
declare const t: T;
const w = x(t); // (s: {}) => { prop1: T1; prop2: T2; prop3: T3; }
The w is not exactly a W... it accepts any input, not just an S. If you really need to narrow the type of input, you'll have to do it yourself by manually specifying the generic parameters:
const w = x<S, {prop1: T1, prop2: T2, prop3: T3}>(t);
which is ugly, or narrowing the resulting w manually by assertion or annotation:
const w: W = x(t);
Anyway, hope that helps. Good luck!
来源:https://stackoverflow.com/questions/48130717/generic-mapping-of-types-in-typescript