TypeScript härleder inte korrekt typ
För detta stycke:
Promise.resolve('kromid')
.then(all(identity))
.then(([a]) => a.splita);
TypeScript säger ingenting om a.splita. Jag förväntar mig att det ska misslyckas med:
Egenskapen 'splita' finns inte på typen 'string'. Menade du 'split'?
Här är resten av koden:
function all<T1, Param>(a1: Res<Param, T1>): (p: Param) => Promise<[T1]>;
function all<T1, T2, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>): (p: Param) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>): (p: Param) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>): (p: Param) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>): (p: Param) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]> {
return param => Promise.all(values.map(obj => obj.apply ? obj(param) : obj));
}
type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>
function identity<T>(a: T): T {
return a;
}
Kan du se var problemet är?
Svar #1
Tack för att du pekade ut problemet som jag inte riktigt förstod. Jag experimenterade lite mer med din kod och här är mina upptäckter. Typinferenz fungerar inte så bra med funktionskomposition i TypeScript:
function identity<T>(a: T): T { return a; }
function toString<T>(a: T): string { return JSON.stringify(a); }
Promise.resolve('kromid')
.then(promiseValue => {
const composedFn = all(identity, toString);
// ^^^^^^^^^^
// T type of `promiseValue` was not inferred properly,
// and was replaces with `any`
return composedFn(promiseValue);
})
.then(([identityResult, toStringResult]) => {
})
Observera att typinferenz misslyckades för en funktion (T) => T men fungerade för en funktion med icke-generisk returtyp (T) => string. Längre fram är identityResult av typen any, medan toStringResult är av rätt typ string.
Jag tror att den verkliga grundorsaken täcks i det här GitHub-ärendet. Förlåt att mitt svar inte är särskilt hjälpsamt. Typinference är det mest komplexa ämnet för mig, därför granskar jag ditt exempel noggrant.
Ritar över mitt gamla svar
Varför händer det?
Jag tror att nyckeln ligger i den här raden:
.then(all(identity))
Det är ekvivalent med
.then(x => all(identity)(x))
snarare än till
.then(x => all(identity(x)))
Typerna kommer att härledas olika i två fall och jag tror att du förväntar dig den senare tolkningen av din kod samtidigt som du får den första.
Hur man uppnår det fel du förväntade dig att se
Variant 1: Om du skriver om koden så att all() får string-löftet och identity() i rätt ordning kommer du att se felet.
Promise.resolve('kromid')
.then(text => all(text)(identity))
.then(([a]) => a.splita);
Som resulterar i:
[ts] Egenskapen 'splita' finns inte på typen 'string'. Menade du 'split'?
Variant 2. Omdisponera parametrarna för den överlagrade all()-funktionen så att det möjliggör att använda den på det sätt du vill:
function all<T1, Param>(p: Param): (a1: Res<Param, T1>) => Promise<[T1]>;
function all<T1, T2, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]>;
function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]> {
return values => Promise.all(values.map((obj: any) => obj.apply ? obj(p) : obj));
}
type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>;
function identity<T>(a: T): T {
return a;
}
Promise.resolve('kromid')
.then(all(identity))
.then(([a]) => a.splita);
Det kommer att uppnå samma resultat:
Jag hoppas att mitt svar är komplett och korrekt nu. :)



