![](/img/trans.png)
[英]Best way to get dynamic type definition in typescript from a configuration object
[英]Typescript dynamic object type definition
假設我有一個 object:
const a = {
section_title: 'Hello world',
section_desc: 'Lorem ipsum dolor sit amet'
}
以及一個通過“前綴”查找所有屬性並返回一個新的 object 的實用程序,這些屬性沒有“前綴” (即 section_title -> title)
function findPropsByPrefix(data, prefix) {
return Object.keys(data)
.filter((key) => key.startsWith(prefix))
.reduce((prev, curr) => {
prev[curr.substr(prefix.length)] = data[curr]
return prev
}, {})
}
// Convert { section_title: '...', section_desc: '...' }
// into { title: '...', desc: '...' }
const b = findPropsByPrefix(a, 'section_')
我應該如何輸入實用程序 function findPropsByPrefix
?
在這個 Typescript 操場上,我正在嘗試使用Record<string, any>
:
function findPropsByPrefix(data: Record<string, any>, prefix: string): Record<string, any> {
...
}
但這會導致錯誤Type 'Record<string, any>' is missing the following properties from type 'B': title, desc
當我將返回值分配給b
時。
extractFieldsByPrefix()
的調用簽名應該是這樣的:
// call signature
function extractFieldsByPrefix<P extends string, T extends Record<string, any>>(
data: T, prefix: P): { [K in keyof T as K extends `${P}${infer R}` ? R : never]: T[K] };
為了跟蹤特定的鍵和值,function 在prefix
類型P
和data
類型T
中都是通用的。 返回類型是使用模板文字類型和鍵重映射從T
和P
計算出來的。 讓我們檢查一下:
{ [K in keyof T as K extends `${P}${infer R}` ? R : never]: T[K] }
這是一個映射類型,它從T
的鍵中查看每個鍵K
對於每個這樣的鍵,它使用條件類型( K extends... ? ... : ...
)來嘗試查看該鍵是否以前綴P
開頭,如果是,則使用條件類型推斷來infer
后綴R
( `${P}${infer R}`
是一個模板文字類型,表示您在將P
連接到R
時獲得的字符串(對於某些R
)。 這是因為模板文字類型支持這種推斷。 如果推理成功,那么我們將舊密鑰K
重新映射到新密鑰R
(所以只是后綴)。 如果推理失敗,那么我們將舊的K
鍵重新映射到never
,這具有完全刪除該屬性的效果。 屬性值類型只是T[K]
,因此R
的新屬性將與K
的舊屬性具有相同的類型。
編譯器通常不可能驗證 function 實現是否滿足具有條件或重新映射類型 output 的通用調用簽名; 有關相關問題,請參閱microsoft/TypeScript#33912 。 現在最好的辦法是使用類似單調用簽名重載的東西,以便對實現進行一些松散的檢查:
// implementation
function extractFieldsByPrefix(data: any, prefix: string) {
return Object.keys(data)
.filter((key) => key.startsWith(prefix))
.reduce<any>((prev, curr) => {
prev[curr.substring(prefix.length)] = data[curr]
return prev
}, {})
}
通過將data
和reduce()
的結果都提供給any
type ,我實質上是在說我不希望編譯器抱怨任何可能的類型問題。 這是最容易做的事情,但這確實意味着您需要小心該實現,因為缺少編譯器錯誤並不意味着該實現沒有錯誤。 例如,如果您編寫了endsWith()
而不是startsWith()
,編譯器仍然不會抱怨,但現在運行時的 output 將不符合調用簽名的返回類型。 所以要小心。
無論如何,讓我們看看它是否有效:
const result = extractFieldsByPrefix(a, 'section_');
/* const result: {
title: string;
desc: string;
} */
console.log(result.title.toUpperCase()) // HELLO WORLD!
const b: B = result; // okay
console.log(b)
/* {
"title": "Hello world!",
"desc": "Lorem ipsum dolor sit amet"
} */
// make sure that dropping fields without the prefix also happens
const also = extractFieldsByPrefix(
{ who: 1, what: 2, when: 3, where: 4, why: 5, how: 6 },
"w"
);
/* const also: {
ho: number;
hat: number;
hen: number;
here: number;
hy: number;
} */
console.log(also);
/* {
"ho": 1,
"hat": 2,
"hen": 3,
"here": 4,
"hy": 5
} */
看起來不錯! Playground 代碼鏈接
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.