我已经创建了一个nameOf用于打字稿的简单助手。
function nameOf<T>(name: Extract<keyof T, string>) {
return name;
}
Run Code Online (Sandbox Code Playgroud)
在功能需要代表一个属性的值的字符串,而没有适当类型的,我可以用它的地方,像这样:expectsKey(nameOf<MyObject>("someMyObjectProperty"))。这意味着即使我不控制expectsKey(key: string)我也可以对传递给它的字符串进行某种类型检查。这样,如果MyObject重命名on属性,则该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']
Run Code Online (Sandbox Code Playgroud)
你不能在你的nameOf函数中直接使用它。这是一个错误,因为类型实例化将被检测为可能是无限的。
declare function nameOf<T>(path: NamesOf<T>): string
Run Code Online (Sandbox Code Playgroud)
但是,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
Run Code Online (Sandbox Code Playgroud)
如果您使用另一条路线并将路径包含在通用约束中,请确保将路径标记为默认路径(因此您在使用该函数时不必指定它)和扩展NameOf<T>(以便用户nameOf不能在钥匙上撒谎)
declare function nameOf<T, P extends NamesOf<T> = NamesOf<T>>(path: P): string
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
371 次 |
| 最近记录: |