ฉันมีห้องสมุดที่ส่งออกประเภทยูทิลิตี้คล้ายกับต่อไปนี้
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
สำหรับ เช่นข้อ จำกัด ประเภทเชิงลบ
Imagine:
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