具有递归、条件和匹配键的 TypeScript 泛型
TypeScript Generics with recursion, conditionals and matching Keys
我有以下带有 2 个通用参数的类型:
type Result<INPUT, SPEC> = ...
Result
的具体类型取决于INPUT
和SPEC
的各种组合。
它应该如何工作:
if(INPUT extends {[key: string]: any}) {
if(SPEC extends {[key: string]: any}) {
Result = {[key in keyof INPUT]: SPEC[key] !== undefined
? Result<INPUT[key], SPEC[key]>
: true
}
} else {
// ...
}
} else {
// ...
}
换言之:
- 如果
INPUT
和SPEC
是对象,那么Result
应该是键为INPUT
的对象。
- 对于
INPUT
的每个键:检查 SPEC
是否包含相同的键
- 如果
true
,则此键的值为 Result<INPUT[key], SPEC[key]>
- 如果
false
,则此键的值为 true
附加信息:
- 如果
INPUT
和SPEC
是对象,那么SPEC
是INPUT
的子集。这意味着:INPUT
可以是 { foo: 42, bar: 42 }
,但 SPEC
只允许包含 foo
或 bar
或两者或为空。
这在某种程度上可以用 TypeScript 实现吗?
根据您的描述,这种类型应该可以解决问题:
export type Result<INPUT, SPEC> = INPUT extends object ? SPEC extends object ? {
[K in keyof INPUT]: SPEC extends Record<K, infer SPECK> ?
Result<INPUT[K], SPECK> :
true
} : never : never;
// r is { foo: true; bar: never; goo: { a: never; b: true; }; }
type r = Result<{
foo: number,
bar: string,
goo: { a: number; b: string }
}, { bar: number, goo: { a: number }}>
您可能遗漏的部分是 SPEC extends Record<K, infer SPECK>
,这会测试类型是否使用键 K
扩展记录并将该键的类型放入 SPECK
。
你没有明确说明在 else 分支上发生了什么(如果任何一个类型参数都不是对象,所以我把 never
放在那里,但你可以根据需要自定义)
如果我没有 100% 正确,请告诉我,我们可以根据需要进行调整。
我有以下带有 2 个通用参数的类型:
type Result<INPUT, SPEC> = ...
Result
的具体类型取决于INPUT
和SPEC
的各种组合。
它应该如何工作:
if(INPUT extends {[key: string]: any}) {
if(SPEC extends {[key: string]: any}) {
Result = {[key in keyof INPUT]: SPEC[key] !== undefined
? Result<INPUT[key], SPEC[key]>
: true
}
} else {
// ...
}
} else {
// ...
}
换言之:
- 如果
INPUT
和SPEC
是对象,那么Result
应该是键为INPUT
的对象。 - 对于
INPUT
的每个键:检查SPEC
是否包含相同的键 - 如果
true
,则此键的值为Result<INPUT[key], SPEC[key]>
- 如果
false
,则此键的值为true
附加信息:
- 如果
INPUT
和SPEC
是对象,那么SPEC
是INPUT
的子集。这意味着:INPUT
可以是{ foo: 42, bar: 42 }
,但SPEC
只允许包含foo
或bar
或两者或为空。
这在某种程度上可以用 TypeScript 实现吗?
根据您的描述,这种类型应该可以解决问题:
export type Result<INPUT, SPEC> = INPUT extends object ? SPEC extends object ? {
[K in keyof INPUT]: SPEC extends Record<K, infer SPECK> ?
Result<INPUT[K], SPECK> :
true
} : never : never;
// r is { foo: true; bar: never; goo: { a: never; b: true; }; }
type r = Result<{
foo: number,
bar: string,
goo: { a: number; b: string }
}, { bar: number, goo: { a: number }}>
您可能遗漏的部分是 SPEC extends Record<K, infer SPECK>
,这会测试类型是否使用键 K
扩展记录并将该键的类型放入 SPECK
。
你没有明确说明在 else 分支上发生了什么(如果任何一个类型参数都不是对象,所以我把 never
放在那里,但你可以根据需要自定义)
如果我没有 100% 正确,请告诉我,我们可以根据需要进行调整。