如何匹配 Typescript 中的嵌套键

2024-02-27

我创建了一个简单的nameOf与打字稿一起使用的助手。

function nameOf<T>(name: Extract<keyof T, string>) {
  return name;
}

在函数需要表示属性键的字符串但类型不正确的地方,我可以像这样使用它:expectsKey(nameOf<MyObject>("someMyObjectProperty"))。这意味着即使我不控制expectsKey(key: string)我可以对传递给它的字符串进行一些类型检查。这样如果一个属性MyObject被更名后,nameOf()调用将显示正常函数在执行之前无法检测到的错误。

是否可以将其扩展到嵌套元素?

即,某种方式进行类型检查nameOf<MyComplexObject>('someProperty[2].someInnerProperty')确保它与类型的结构匹配MyComplexObject?


直接地?不可以。您无法创建连接属性来在 TS 中创建新字符串,而这是此功能所必需的。

但是,您可以通过映射类型获得类似的功能。

interface MyObject {
  prop: {
    arr?: { inner: boolean }[]
    other: string
    method(): void
  }
}

// Creates [A, ...B] if B is an array, otherwise never
type Join<A, B> = B extends any[]
  ? ((a: A, ...b: B) => any) extends ((...args: infer U) => any) ? U : never
  : never

// Creates a union of tuples descending into an object.
type NamesOf<T> = { 
  [K in keyof T]: [K] | Join<K, NamesOf<NonNullable<T[K]>>>
}[keyof T]

// ok
const keys: NamesOf<MyObject> = ['prop']
const keys2: NamesOf<MyObject> = ['prop', 'arr', 1, 'inner']

// error, as expected
const keys3: NamesOf<MyObject> = [] // Need at least one prop
const keys4: NamesOf<MyObject> = ['other'] // Wrong level!
// Technically this maybe should be allowed...
const keys5: NamesOf<MyObject> = ['prop', 'other', 'toString']

您不能直接在您的nameOf功能。这是一个错误,因为类型实例化将被检测为可能是无限的。

declare function nameOf<T>(path: NamesOf<T>): string

但是,您可以使用NamesOf如果你让 TypeScript 推迟其解析,直到你实际使用该函数。您可以相当轻松地做到这一点,方法是将其作为通用默认值,或者将参数类型包装在条件中(这提供了防止使用nameOf当类型是原始类型时)

interface MyObject {
  prop: {
    arr?: { inner: boolean }[]
    other: string
    method(): void
  },
  prop2: {
    something: number
  }
}

// Creates [A, ...B] if B is an array, otherwise never
type Join<A, B> = B extends any[]
  ? ((a: A, ...b: B) => any) extends ((...args: infer U) => any) ? U : never
  : never

// Creates a union of tuples descending into an object.
type NamesOf<T> = { 
  [K in keyof T]: [K] | Join<K, NamesOf<NonNullable<T[K]>>>
}[keyof T]

declare function nameOf<T>(path: T extends object ? NamesOf<T> : never): string

const a = nameOf<MyObject>(['prop', 'other']) // Ok
const c = nameOf<MyObject>(['prop', 'arr', 3, 'inner']) // Ok
const b = nameOf<MyObject>(['prop', 'something']) // Error, should be prop2

如果您采用其他路线并将路径包含在通用约束中,请务必将该路径标记为默认路径(这样您在使用该函数时不必指定它)和扩展路径NameOf<T>(以便用户nameOf不能对钥匙撒谎)

declare function nameOf<T, P extends NamesOf<T> = NamesOf<T>>(path: P): string
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何匹配 Typescript 中的嵌套键 的相关文章

随机推荐