J'ai une bibliothèque qui exporte un type d'utilitaire similaire au suivant:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Ce type d'utilitaire vous permet de déclarer une fonction qui fonctionnera comme une "action". Il reçoit un argument générique étant Modell'action contre laquelle l'action va fonctionner.
L' dataargument de "l'action" est ensuite tapé avec un autre type d'utilitaire que j'exporte;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
Le Statetype d'utilitaire prend essentiellement le Modelgénérique entrant , puis crée un nouveau type où toutes les propriétés de type Actionont été supprimées.
Par exemple, voici une mise en œuvre de base des terres utilisateur de ce qui précède;
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;
}
}
Ce qui précède fonctionne très bien. 👍
Cependant, il y a un cas avec lequel je me bats, en particulier lorsqu'une définition de modèle générique est définie, ainsi qu'une fonction d'usine pour produire des instances du modèle générique.
Par exemple;
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;
}
};
}
Dans l'exemple ci-dessus, je m'attends à ce que l' dataargument soit tapé là où l' doSomethingaction a été supprimée et que la valuepropriété générique existe toujours. Ce n'est cependant pas le cas - la valuepropriété a également été retirée par notre Stateservice public.
Je crois que la cause de ceci est qu'il Test générique sans aucune restriction de type / rétrécissement qui lui est appliqué, et donc le système de type décide qu'il intersecte avec un Actiontype et le supprime ensuite du datatype d'argument.
Existe-t-il un moyen de contourner cette restriction? J'ai fait quelques recherches et j'espérais qu'il y aurait un mécanisme dans lequel je pourrais dire que Tc'est tout sauf un Action. c'est-à-dire une restriction de type négatif.
Imaginer:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Mais cette fonctionnalité n'existe pas pour TypeScript.
Quelqu'un connaît-il un moyen de faire fonctionner cela comme je m'y attendais?
Pour faciliter le débogage, voici un extrait de code complet:
// 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;
}
};
}
Vous pouvez jouer avec cet exemple de code ici: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14