Ho una libreria che esporta un tipo di utilità simile al seguente:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Questo tipo di utilità consente di dichiarare una funzione che verrà eseguita come "azione". Riceve un argomento generico in base al Modelquale l'azione agirà.
L' dataargomento di "azione" viene quindi digitato con un altro tipo di utilità che esporto;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
Il Statetipo di utilità prende sostanzialmente il Modelgenerico in entrata e quindi crea un nuovo tipo in cui tutte le proprietà di tipo Actionsono state rimosse.
Ad esempio, qui è un'implementazione di terra utente di base di quanto sopra;
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;
}
}
Quanto sopra funziona molto bene. 👍
Tuttavia, c'è un caso con cui sto lottando, in particolare quando viene definita una definizione di modello generico, insieme a una funzione di fabbrica per produrre istanze del modello generico.
Per esempio;
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;
}
};
}
Nell'esempio sopra mi aspetto che l' dataargomento venga digitato nel punto in cui l' doSomethingazione è stata rimossa e la valueproprietà generica esiste ancora. Questo comunque non è il caso: la valueproprietà è stata rimossa anche dalla nostra Stateutility.
Credo che la causa Tsia generica senza che vi siano applicate restrizioni / restringimenti del tipo, e quindi il sistema dei tipi decide che si interseca con un Actiontipo e successivamente lo rimuove dal datatipo di argomento.
C'è un modo per aggirare questa limitazione? Ho fatto qualche ricerca e spero che ci sarebbe un meccanismo in cui potrei affermare che Tè tutto tranne che per un Action. cioè una restrizione di tipo negativa.
Immaginare:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Ma questa funzione non esiste per TypeScript.
Qualcuno sa come posso farlo funzionare come mi aspetto?
Per facilitare il debug ecco uno snippet di codice completo:
// 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;
}
};
}
Puoi giocare con questo esempio di codice qui: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14