我创建了一个简单的 nameOf
与 typescript 一起使用的助手。
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
但是,如果您让 TypeScript 推迟其解析直到您实际使用该函数,则可以使用 NamesOf
。您可以很容易地做到这一点,方法是将其作为通用默认值包含在内,或者将参数类型包装在条件中(这提供了在类型为原始类型时防止使用 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
的用户就不能对 key 撒谎)
declare function nameOf<T, P extends NamesOf<T> = NamesOf<T>>(path: P): string
关于typescript - 如何匹配 typescript 中的嵌套键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56605358/