Saya memiliki perpustakaan yang mengekspor jenis utilitas yang mirip dengan yang berikut:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
Jenis utilitas ini memungkinkan Anda untuk mendeklarasikan fungsi yang akan berfungsi sebagai "tindakan". Ia menerima argumen umum Modelbahwa tindakan itu akan dilawan.
The dataArgumen dari "tindakan" kemudian diketik dengan jenis utilitas lain yang saya ekspor;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
The Stateutilitas jenis pada dasarnya mengambil masuk Modelgenerik dan kemudian menciptakan jenis baru di mana semua properti yang bertipe Actiontelah dihapus.
Untuk misalnya di sini adalah implementasi dasar pengguna lahan di atas;
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;
}
}
Di atas bekerja dengan sangat baik. π
Namun, ada kasus yang saya perjuangkan, khususnya ketika definisi model generik didefinisikan, bersama dengan fungsi pabrik untuk menghasilkan contoh-contoh model generik.
Sebagai contoh;
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;
}
};
}
Pada contoh di atas saya berharap dataargumen akan diketik di mana doSomethingtindakan telah dihapus, dan valueproperti generik masih ada. Namun ini tidak terjadi - valueproperti juga telah dihapus oleh Stateutilitas kami .
Saya percaya penyebabnya Tadalah generik tanpa batasan jenis / penyempitan yang diterapkan padanya, dan karena itu sistem tipe memutuskan bahwa ia bersinggungan dengan suatu Actiontipe dan kemudian menghapusnya dari datatipe argumen.
Apakah ada cara untuk mengatasi batasan ini? Saya telah melakukan beberapa penelitian dan berharap akan ada beberapa mekanisme di mana saya dapat menyatakan bahwa Tada kecuali untuk Action. yaitu pembatasan tipe negatif.
Membayangkan:
function modelFactory<T extends any except Action<any>>(value: T): UserDefinedModel<T> {
Tetapi fitur itu tidak ada untuk TypeScript.
Apakah ada yang tahu cara saya bisa membuat ini berfungsi seperti yang saya harapkan?
Untuk membantu debugging di sini adalah cuplikan kode lengkap:
// 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;
}
};
}
Anda dapat bermain dengan contoh kode ini di sini: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14