Typescript 如何根据记录键的值筛选记录键?
我有这样的记录:Typescript 如何根据记录键的值筛选记录键?,typescript,generics,types,generic-programming,typescript-generics,Typescript,Generics,Types,Generic Programming,Typescript Generics,我有这样的记录: interface TheRecord extends TheRecordType { a: { typeA: 'string' }, b: { typeB: 123 }, c: { typeA: 'string' }, } type TheRecordType = Record<string, TypeA | TypeB> type TypeA = { typeA: string } type TypeB = { typeB: number }
interface TheRecord extends TheRecordType {
a: { typeA: 'string' },
b: { typeB: 123 },
c: { typeA: 'string' },
}
type TheRecordType = Record<string, TypeA | TypeB>
type TypeA = { typeA: string }
type TypeB = { typeB: number }
下面是我如何尝试过滤掉它们
type FilteredForTypeA = { [k in keyof TheRecord]: TheRecord[k] extends TypeA ? TheRecord[k] : never }
这里发生了一些事情,所以我将给出一个答案,因为它不是我发现的相关现有问题的直接副本 当您的类型具有索引签名时,如果对象的“已知”文本键是索引签名的子类型,则很难仅提取它们。也就是说,
keyof{[k:string]:any,foo:any}
只是string
,而“foo”
完全包含在其中。您可以使用条件类型技巧仅提取已知的文本键,如所示:
类型KnownKeys=提取;
另一方面,您只需要其值具有与特定类型匹配的属性的键。这在映射条件查找中是可行的,如所示:
type KeysMatching={[K in keyof T]:T[K]扩展V?K:never}[keyof T];
把这些放在一起,你会得到:
type KnownKeysMatching<T, V> = KeysMatching<Pick<T, KnownKeys<T>>, V>
类型knownkeymatching=KeysMatching
您可以验证它是否按照我认为您的意图工作:
function doStuff(arg: KnownKeysMatching<TheRecord, TypeA>): void {
}
doStuff('a'); // okay
doStuff('b'); // error!
doStuff('c'); // okay
doStuff('d'); // error!
函数doStuff(arg:knownkeymatching):无效{
}
多斯塔夫('a');//可以
doStuff('b');//错误!
doStuff('c');//可以
doStuff('d');//错误!
请注意,根据需要,arg
不能是'b'
,但也不能是'd'
或任何其他“未知”字符串,即使记录
具有字符串索引签名。如果您需要'd'
的其他行为,这是可以做到的,但这似乎超出了问题的范围
希望有帮助;祝你好运
使用稍微修改过的
KnownKeys
版本来排除值也从不扩展的键,您最终会得到以下结果
interface TheRecord extends TheRecordType {
a: { typeA: 'string' },
b: { typeB: 123 },
c: { typeA: 'string' },
}
type TheRecordType = Record<string, TypeA | TypeB>
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : T[K] extends never ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
type TypeA = { typeA: string }
type TypeB = { typeB: number }
function doStuff(arg: KnownKeys<FilteredForTypeA>): void {
}
type FilteredForTypeA = { [k in keyof TheRecord]: TheRecord[k] extends TypeA ? TheRecord[k] : never }
doStuff('b'); // error!
接口记录扩展了记录类型{
a:{typeA:'string'},
b:{typeB:123},
c:{typeA:'string'},
}
类型记录类型=记录
类型KnownKeys={
[K in keyof T]:字符串扩展K?从不:数字扩展K?从不:T[K]扩展从不?从不:K
}扩展{[U]in keyof T]:推断U}?U:从来没有;
类型TypeA={TypeA:string}
类型TypeB={TypeB:number}
函数doStuff(arg:KnownKeys):void{
}
type FilteredForTypeA={[k in key of TheRecord]:TheRecord[k]扩展TypeA?TheRecord[k]:never}
doStuff('b');//错误!
可能是ugh的副本,除非您在其中有索引签名。您可能需要先使用KnownKeys
,就像在Thanx@jcalz中一样!莱姆文摘。。
type KnownKeysMatching<T, V> = KeysMatching<Pick<T, KnownKeys<T>>, V>
function doStuff(arg: KnownKeysMatching<TheRecord, TypeA>): void {
}
doStuff('a'); // okay
doStuff('b'); // error!
doStuff('c'); // okay
doStuff('d'); // error!
interface TheRecord extends TheRecordType {
a: { typeA: 'string' },
b: { typeB: 123 },
c: { typeA: 'string' },
}
type TheRecordType = Record<string, TypeA | TypeB>
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : T[K] extends never ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
type TypeA = { typeA: string }
type TypeB = { typeB: number }
function doStuff(arg: KnownKeys<FilteredForTypeA>): void {
}
type FilteredForTypeA = { [k in keyof TheRecord]: TheRecord[k] extends TypeA ? TheRecord[k] : never }
doStuff('b'); // error!