Tengo una biblioteca que exporta un tipo de utilidad similar al siguiente:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Este tipo de utilidad le permite declarar una función que se realizará como una "acción". Recibe un argumento genérico Model
contra el cual operará la acción.
El data
argumento de la "acción" se escribe con otro tipo de utilidad que exporto;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
El State
tipo de utilidad básicamente toma el Model
genérico entrante y luego crea un nuevo tipo donde Action
se han eliminado todas las propiedades que son de tipo .
Por ejemplo, aquí hay una implementación básica de usuario de lo anterior;
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;
}
}
Lo anterior está funcionando muy bien. 👍
Sin embargo, hay un caso con el que estoy luchando, específicamente cuando se define una definición de modelo genérico, junto con una función de fábrica para producir instancias del modelo genérico.
Por ejemplo;
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;
}
};
}
En el ejemplo anterior, espero que el data
argumento se escriba donde doSomething
se eliminó la acción y la value
propiedad genérica aún existe. Sin embargo, este no es el caso: value
nuestra propiedad también ha eliminado la propiedad State
.
Creo que la causa de esto es que T
es genérico sin que se apliquen restricciones / restricciones de tipo, y por lo tanto, el sistema de tipos decide que se cruza con un Action
tipo y posteriormente lo elimina del data
tipo de argumento.
¿Hay alguna forma de evitar esta restricción? He investigado un poco y esperaba que hubiera algún mecanismo en el que pudiera afirmar que T
hay alguno, excepto un Action
. es decir, una restricción de tipo negativa.
Imagina:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Pero esa característica no existe para TypeScript.
¿Alguien sabe de alguna manera que podría hacer que esto funcione como lo espero?
Para ayudar a la depuración, aquí hay un fragmento de código 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;
}
};
}
Puedes jugar con este ejemplo de código aquí: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14
fuente
Exactamente como dijiste, el problema es que todavía no tenemos restricciones negativas. También espero que puedan obtener esa característica pronto. Mientras espero, propongo una solución como esta:
fuente
count
yvalue
siempre hará que el compilador sea infeliz. Para solucionarlo, puede intentar algo como esto:Como
Partial
se está utilizando el tipo de utilidad, estará bien en el caso de que eltransform
método no esté presente.Stackblitz
fuente
Generalmente leo eso dos veces y no entiendo completamente lo que quieres lograr. Según tengo entendido, desea omitir
transform
del tipo que se le da exactamentetransform
. Para lograr eso es simple, necesitamos usar Omitir :No estoy seguro de si esto es lo que quería debido a la gran complejidad que ha dado en los tipos de utilidad adicionales. Espero eso ayude.
fuente