typescript - Förväntade 3 typargument men fick 1, men det borde härleda 2 typer
Jag undrar hur man korrekt härleder den andra och tredje mallen av min funktion
antar ett enkelt gränssnitt
interface ISome {
a: string;
b?: {
c: string;
};
}
Följande fungerar:
function pathBuilder<
K1 extends keyof ISome,
K2 extends keyof NonNullable<ISome[K1]>
>(p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathTest = pathBuilder("b", "c"); // ---> "b.c" and intellisense works on parameters
men jag behöver generalisera funktionen för att fungera genom att ange en annan typ ( Jag vill inte skicka in en objektinstans för att ange typen )
Så, följande fungerar inte:
function pathBuilder<
T,
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>
>(p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathTest = pathBuilder<ISome>("b", "c"); // ERROR: Expected 3 type arguments, but got 1.ts(2558)
Det verkar som att den andra och tredje mallargumentet för funktionen inte härleds från det första, men det borde det eftersom när jag direkt specificerade typen som T=ISome, fungerade det.
Jag är inte säker på om det finns något språknøkkelord för att få det att fungera, men mallen bör fungera exakt för det: ange en okänd typ.
REDIGERA
Faktiskt hittade jag detta sätt, men det kräver extra kodning som jag skulle undvika om möjligt
function pathBuilder<T>() {
return <
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
};
}
const pathTest = pathBuilder<ISome>()("b", "c");
Svar #1
Från och med TS5.0 finns det ingen partiell typargumentinferens som begärts i microsoft/TypeScript#10571 och microsoft/TypeScript#26242. Antingen låter du kompilatorn försöka inferera alla typparametrar, eller så specificerar du alla typparametrar. (Jo, det finns standardtypargument men det ger dig inte vad du vill: du vill inferera de typparametrar du lämnar ut, inte tilldela en standard typ till dem). Det har funnits flera förslag för att åtgärda detta, vid ms/TS#23696 och ms/TS#22368, men hittills har inget blivit helt godkänt eller sammanslaget.
För nu finns det därför bara tillfälliga lösningar. De två som jag kan komma på är att använda en dummy-funktionsparameter eller att använda currying.
Dummyparameter-versionen:
function pathBuilderDummy<
T,
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(dummy: T, p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathDummyTest = pathBuilderDummy(null! as ISome, "b", "c");
Här gör vi det du sa att du inte ville göra... skicka in en parameter av typen T. Men eftersom det bara är en dummy-parameter och inte används vid körning, spelar det bara roll vad typ-systemet tror att det är. Den faktiska typen av värdet du skickar in spelar ingen roll. Så du kan helt enkelt skicka in null och använda en typbesked för att välja T.
Lösningen med curryfurerad funktion:
const pathBuilderCurry =
<T>() => <
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathCurryTest = pathBuilderCurry<ISome>()("b", "c")
Här returnerar du en funktion som returnerar en annan funktion. Den första funktionen tar inga värdeparametrar men den tar den typparameter du vill specificera. Den returnerar sedan en funktion där T är specificerad men de andra typparametrarna härleds.
Ingen av lösningarna är perfekt, men de är det bästa vi kan göra för tillfället.
