Ich habe eine Bibliothek, die einen Dienstprogramm-Typ ähnlich dem folgenden exportiert:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Mit diesem Dienstprogramm können Sie eine Funktion deklarieren, die als "Aktion" ausgeführt wird. Es erhält ein allgemeines Argument dafür, Modeldass die Aktion gegen sie vorgehen wird.
Das dataArgument der "Aktion" wird dann mit einem anderen Dienstprogrammtyp eingegeben, den ich exportiere.
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
Der StateDienstprogrammtyp verwendet im Wesentlichen das eingehende ModelGenerikum und erstellt dann einen neuen Typ, bei dem alle Eigenschaften vom Typ Actionentfernt wurden.
Zum Beispiel ist hier eine grundlegende Benutzerland-Implementierung des Obigen;
interface MyModel {
counter: number;
increment: Action<Model>;
}
const myModel = {
counter: 0,
increment: (data) => {
data.counter; // Exists and typed as `number`
data.increment; // Does not exist, as stripped off by State utility
return data;
}
}
Das obige funktioniert sehr gut. 👍
Es gibt jedoch einen Fall, mit dem ich zu kämpfen habe, insbesondere wenn eine generische Modelldefinition definiert ist, zusammen mit einer Factory-Funktion, um Instanzen des generischen Modells zu erzeugen.
Zum Beispiel;
interface MyModel<T> {
value: T; // 👈 a generic property
doSomething: Action<MyModel<T>>;
}
function modelFactory<T>(value: T): MyModel<T> {
return {
value,
doSomething: data => {
data.value; // Does not exist 😭
data.doSomething; // Does not exist 👍
return data;
}
};
}
Im obigen Beispiel erwarte ich, dass das dataArgument dort eingegeben wird, wo die doSomethingAktion entfernt wurde und die generische valueEigenschaft noch vorhanden ist. Dies ist jedoch nicht der Fall - die valueEigenschaft wurde auch von unserem StateDienstprogramm entfernt.
Ich glaube, die Ursache dafür ist, dass Tes generisch ist, ohne dass Typeinschränkungen / -einengungen angewendet werden, und daher entscheidet das Typsystem, dass es sich mit einem ActionTyp überschneidet, und entfernt ihn anschließend aus dem dataArgumenttyp.
Gibt es eine Möglichkeit, diese Einschränkung zu umgehen? Ich habe einige Nachforschungen angestellt und gehofft, dass es einen Mechanismus geben würde, in dem ich feststellen könnte, dass Tes einen außer einem gibt Action. dh eine negative Typbeschränkung.
Vorstellen:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Diese Funktion ist für TypeScript jedoch nicht vorhanden.
Kennt jemand einen Weg, wie ich das so machen kann, wie ich es erwartet habe?
Um das Debuggen zu erleichtern, finden Sie hier ein vollständiges Code-Snippet:
// Returns the keys of an object that match the given type(s)
type KeysOfType<A extends object, B> = {
[K in keyof A]-?: A[K] extends B ? K : never
}[keyof A];
// Filters out an object, removing any key/values that are of Action<any> type
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
// My utility function.
type Action<Model extends object> = (data: State<Model>) => State<Model>;
interface MyModel<T> {
value: T; // 👈 a generic property
doSomething: Action<MyModel<T>>;
}
function modelFactory<T>(value: T): MyModel<T> {
return {
value,
doSomething: data => {
data.value; // Does not exist 😭
data.doSomething; // Does not exist 👍
return data;
}
};
}
Sie können mit diesem Codebeispiel hier spielen: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14