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 Model
bahwa tindakan itu akan dilawan.
The data
Argumen dari "tindakan" kemudian diketik dengan jenis utilitas lain yang saya ekspor;
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
The State
utilitas jenis pada dasarnya mengambil masuk Model
generik dan kemudian menciptakan jenis baru di mana semua properti yang bertipe Action
telah 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 data
argumen akan diketik di mana doSomething
tindakan telah dihapus, dan value
properti generik masih ada. Namun ini tidak terjadi - value
properti juga telah dihapus oleh State
utilitas kami .
Saya percaya penyebabnya T
adalah generik tanpa batasan jenis / penyempitan yang diterapkan padanya, dan karena itu sistem tipe memutuskan bahwa ia bersinggungan dengan suatu Action
tipe dan kemudian menghapusnya dari data
tipe 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 T
ada 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