我想定義一個函式是否應該通過介面包含一個引數。我正在開發的庫需要生成許多不同的方法,而對這些方法進行硬編碼需要太多的維護;所以我認為型別是定義這些東西的好地方。
也許這最好用代碼來解釋。這是一個抽象了一些休息 API 的庫:
interface RequestInterface {
endpoint: string
body?: unknown
}
interface GetPosts extends RequestInterface {
endpoint: '/posts'
body: never
}
interface CreatePost extends RequestInterface {
endpoint: '/posts'
body: string
}
function Factory<R extends RequestInterface> (endpoint: R['endpoint']) {
return (body?: R['body']): void => {
console.log(`Hitting ${endpoint} with ${body}`)
}
}
const myLibrary = {
getPosts: Factory<GetPosts>('/posts'),
createPosts: Factory<CreatePost>('/posts'),
}
myLibrary.getPosts('something') // => Correctly errors
myLibrary.createPosts(999) // => Correctly errors
myLibrary.createPosts() // => I want this to error
在上面,我在我的介面中定義了特定型別請求的endpoint和body。盡管 TypeScript 編譯器正確地防止我傳遞錯誤的引數型別,但它并不能防止我在需要時不傳遞值。
我理解為什么 TypeScript 不會出錯(因為工廠中定義的方法可以根據我的型別進行未定義),但我認為上面的代碼是描述我想要實作的目標的好方法:一個快速、宣告性的方法庫滿足特定型別。
一個可能的解決方案
如果我愿意從兩個單獨的介面(一個或另一個)擴展我的介面,那么我可以使用Construct Signatures實作接近我想要的東西:
interface RequestInterface {
endpoint: string
call: () => void
}
interface RequestInterfaceWithBody {
endpoint: string
call: {
(body: any): void
}
}
interface GetPosts extends RequestInterface {
endpoint: '/posts'
}
interface CreatePost extends RequestInterfaceWithBody {
endpoint: '/posts'
call: {
(body: string): void
}
}
function Factory<R extends RequestInterface|RequestInterfaceWithBody> (endpoint: R['endpoint']): R['call'] {
return (body): void => {
console.log(`Hitting ${endpoint} with ${body}`)
}
}
const myLibrary = {
getPosts: Factory<GetPosts>('/posts'),
createPosts: Factory<CreatePost>('/posts'),
}
myLibrary.getPosts() // => Correctly passes
myLibrary.getPosts('something') // => Correctly errors
myLibrary.createPosts(999) // => Correctly errors
myLibrary.createPosts() // => Correctly errors
myLibrary.createPosts('hi') // => Correctly passes
除了在擴展任何東西之前我需要在兩個“超級”型別之間進行選擇這一事實之外,一個主要問題是構造簽名引數不是很容易訪問。
盡管示例中沒有演示,但我創建的型別也在我的代碼庫中的其他地方使用,并且body是可訪問的(即GetPosts['body'])。有了上面的內容,訪問起來并不容易,我可能需要創建一個單獨的可重用型別定義來實作相同的目的。
uj5u.com熱心網友回復:
你的初始型別幾乎到位。需要做兩個改動:
- 制作
body的GetPosts型別void - 制作
body所需的回傳功能
interface RequestInterface {
endpoint: string;
body?: unknown;
}
interface GetPosts extends RequestInterface {
endpoint: "/posts";
body: void;
}
interface CreatePost extends RequestInterface {
endpoint: "/posts";
body: string;
}
function Factory<R extends RequestInterface>(endpoint: R["endpoint"]) {
return (body: R["body"]): void => {
console.log(`Hitting ${endpoint} with ${body}`);
};
}
const myLibrary = {
getPosts: Factory<GetPosts>("/posts"),
createPosts: Factory<CreatePost>("/posts"),
};
// @ts-expect-error
myLibrary.getPosts("something");
// @ts-expect-error
myLibrary.createPosts(999);
// @ts-expect-error
myLibrary.createPosts();
myLibrary.getPosts();
myLibrary.createPosts("Hello, StackOverflow!");
TS游樂場
解釋
nevertype 告訴編譯器這不應該發生。所以,如果有人試圖使用GetPosts,這是一個錯誤,因為它永遠不應該發生。void(undefined在這種情況下應該也可以)告訴價值不應該存在。
body在回傳的函式中使required 使其成為必需的。但是因為它是voidfor GetPosts,你可以稱它為 likemyLibrary.getPosts(undefined)或簡單地myLibrary.getPosts()等效
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/367434.html
標籤:javascript 打字稿
下一篇:按揭計算器/我可以借多少錢
