我正在為網路作業者撰寫一個小的 RPC 庫,它需要消費者遍歷遠程參考。要訪問參考,您必須使用IReference.property(...path: string[]).
例如,如果我有一個看起來像的源物件 { foo: { bar: { value: 'foobar' }}}
然后我將訪問內部值 await ref.property('foo', 'bar', 'value').value()
我想要的是回傳值.value()是 aPromise的值。
我設法撰寫了一種型別,允許我在property方法中有一個路徑段,但是如何添加更多?
export interface IReference<T> {
property<K extends keyof T | ((...args: any) => any)>(key: K): K extends keyof T ? IReference<T[K]> : any;
value(): T extends (...args: any) => any ? any : Promise<T>;
}
const data = { foo: { bar: { value: 'foobar' }}}
declare const ref0: IReference<typeof data>
const ref1 = ref0.property('foo', 'bar', 'value')
const value = await ref1.value() // should be string
打字稿游樂場
uj5u.com熱心網友回復:
您可以使用遞回條件型別來遍歷路徑元組并檢索目標 - 遞回直到我們用完路徑。
我不太確定 value() 的簽名在你的例子中是什么 - 我相信這可能只是 Promise,但也許我錯過了一些東西。
編輯 - 更新以添加自動完成功能。基于@jcalz 對他的回答給出的一些回答,我已經調整了這些回答,為引數添加了注釋和名稱。
物件屬性路徑的 TypeScript 型別定義
這里的要點是我們需要建立一個包含有效屬性訪問器序列的元組聯合——即
{ a: { b: { c: 'foo' } } }
您有以下元組:
['a']
['a', 'b']
['a', 'b', 'c']
另一件要注意的事情是,任何遞回型別都需要某種退出標準,以便編譯器知道您不會無限遍歷。鑒于我們正在深入研究一個物件——而不是迭代一個陣列——我們可以使用一個計數器并在每次遞回時遞減它。當計數器達到最終值時never- 我們放棄遞回。
如果未提供,此處的默認限制設定為 10。( RecursiveDepthCounter extends number = 10)。
type PreviousNumber = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]
type Paths<Target extends object, RecursiveDepthCounter extends number = 10> =
// Check if we've hit recursive depth, if so, bail
[RecursiveDepthCounter] extends [never]
? never
: {
[key in keyof Target]: Target[key] extends infer TargetChild
? TargetChild extends object
// If we have an object at TargetChild, then we can either access this object ([key])
// or we need to recurse by calling Paths again, decrementing our recursive depth counter and appending
// to the tuple of acceptable keys
? [key] | [key, ...Paths<TargetChild, PreviousNumber[RecursiveDepthCounter]>]
// If we don't have an object, only key is permissable
: [key]
// If we can't infer Target[key], only allow [key]
: [key]
// Access resulting object via keyof Target to remove nesting
}[keyof Target];
type PropertyAtPath<Target extends unknown, Path extends readonly unknown[]> =
// Base recursive case, no more paths to traverse
Path extends []
// Return target
? Target
// Here we have 1 or more paths to access
: Path extends [infer TargetPath, ...infer RemainingPaths]
// Check Target can be accessed via this path
? TargetPath extends keyof Target
// Recurse and grab paths
? PropertyAtPath<Target[TargetPath], RemainingPaths>
// Target path is not keyof Target
: never
// Paths could not be destructured
: never;
export type IReference<T extends object> = {
property<P extends Paths<T>>(...paths: [...P]): IReference<PropertyAtPath<T, P>>;
value(): Promise<T>;
}
const data = { foo: { bar: { value: 'foobar' } } };
declare const ref0: IReference<typeof data>;
const ref1 = ref0.property('foo', 'bar');
const value = await ref1.value() // is string
TS 游樂場:hhttps://tsplay.dev/WGkyoW
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/343866.html
標籤:打字稿
上一篇:僅使用打字稿庫
