У меня есть библиотека, которая экспортирует тип утилиты, подобный следующему:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Этот тип утилиты позволяет вам объявить функцию, которая будет выполнять «действие». Он получает общий аргумент, против Model
которого будет действовать действие.
Затем data
аргумент «действия» набирается с помощью другого типа утилиты, который я экспортирую;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
Тип State
утилиты в основном принимает входящий Model
универсальный тип, а затем создает новый тип, в котором все свойства данного типа Action
были удалены.
Например, вот базовая пользовательская реализация земли выше;
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;
}
}
Выше работает очень хорошо. 👍
Однако есть один случай, с которым я борюсь, особенно когда определено определение общей модели вместе с фабричной функцией для создания экземпляров общей модели.
Например;
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;
}
};
}
В приведенном выше примере я ожидаю, что data
аргумент будет напечатан там, где doSomething
действие было удалено, а универсальное value
свойство все еще существует. Это, однако, не так - value
собственность также была удалена нашей State
утилитой.
Я полагаю, что причина этого заключается в том, что он T
является общим без каких-либо ограничений / сужений типов, и поэтому система типов решает, что он пересекается с Action
типом, а затем удаляет его из data
типа аргумента.
Есть ли способ обойти это ограничение? Я сделал некоторые исследования и надеется , что будет какой - то механизм , в котором я мог бы утверждать , что T
любая , кроме для Action
. т.е. отрицательное ограничение типа.
Представить:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Но эта функция не существует для TypeScript.
Кто-нибудь знает, как я могу заставить это работать так, как я ожидаю?
Чтобы помочь отладке, вот полный фрагмент кода:
// 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;
}
};
}
Вы можете поиграть с этим примером кода здесь: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14