如何编写使用键数组从另一种类型中提取属性的 TypeScript 类型
How to write a TypeScript type that extracts properties from another type using an array of keys
我正在尝试编写一个 TypeScript 类型,将一个类型的键元组转换为仅具有这些属性的类型。以下代码是我得到的最接近的代码:
type Primitive = string | number | boolean;
type ExtractProps<TObject, TKeys extends (keyof TObject)[], TExtends = any> = {
[Index in keyof TKeys as TKeys[Index] extends keyof TObject ? (TObject[TKeys[Index]] extends TExtends ? TKeys[Index] : never) : never ]:
TKeys[Index] extends keyof TObject ? TObject[TKeys[Index]] : never
};
type Type = {
'string': string;
'number': number;
'boolean': boolean;
'array': Array<any>
}
type Test1 = ExtractProps<Type, ['string', 'number', 'array']>;
// Output:
// type Test1 = {
// string: string | number | any[];
// number: string | number | any[];
// array: string | number | any[];
// }
// Desired Output:
// type Test1 = {
// string: string;
// number: number;
// array: any[];
// }
type Test2 = ExtractProps<Type, ['string', 'number', 'array'], Primitive>;
// Output:
// type Test2 = {
// string: string;
// number: number;
// }
// Desired Output:
// type Test2 = {
// string: string;
// number: number;
// }
type Test3 = ExtractProps<Type, ['string', 'number'], Primitive>;
// Output:
// type Test3 = {
// string: string | number;
// number: string | number;
// }
// Desired Output:
// type Test3 = {
// string: string;
// number: number;
// }
只有当重新映射表达式对其中一个键的计算结果为 never
时,我才能使该类型正常工作(如 Type2 中的 array
和 属性 类型一样不扩展 Primitive
)。有没有一种写法 ExtractProps
使得 Type1 和 Type3 有正确的 属性 类型?
你找的好像是the Pick<T, K>
utility type which produces a type from T
with only a subset of the known properties, specifically those whose keys are in K
, and something we can call PickByValue<T, V>
(as requested in ) which produces a type from T
with only a subset of the known properties, specifically those whose values are in V
. We can use key remapping via as
的组合写成PickByValue
:
type PickByValue<T, V> =
{ [K in keyof T as T[K] extends V ? K : never]: T[K] };
然后合并得到ExtractProps
:
type ExtractProps<T, K extends keyof T, V = any> =
PickByValue<Pick<T, K>, V>
我们来测试一下:
type Test1 = ExtractProps<Type, 'string' | 'number' | 'array'>;
/* type Test1 = {
string: string;
number: number;
array: any[];
} */
type Test2 = ExtractProps<Type, 'string' | 'number' | 'array', Primitive>;
/* type Test2 = {
string: string;
number: number;
} */
type Test3 = ExtractProps<Type, 'string' | 'number', Primitive>;
/* type Test3 = {
string: string;
number: number;
} */
看起来不错。
请注意,您也可以通过组合 Pick
和 PickByValue
的功能而不使用它们来直接实现 ExtractProps
:
type ExtractProps<T, K extends keyof T, V = any> =
{ [P in keyof T as T[P] extends V ? Extract<K, P> : never]: T[P]
}
最后,请注意,如问题中所写,您的 ExtractProps
版本希望其第二个参数是 arraylike 类型,其元素是键。你可以做
那:
type ExtractPropsArray<T, KS extends Array<keyof T>, V = any> =
PickByValue<Pick<T, KS[number]>, V>;
但实现不关心数组类型的细节,只关心其元素类型的并集。它只是丢弃了关于 KS
的大部分信息。我不建议编写一个需要它实际上不需要的信息的类型。你总是可以在使用数组之前解包它,比如 ExtractProps<T, KS[number], V>
,而当你从类似键的类型开始时发明一个无用的数组包装器有点愚蠢,比如 ExtractPropsArray<T, K[], V>
或 ExtractPropsArray<T, [K, K], V>
。但这最终更多的是一种意见,而不是权威来源,所以你应该按照你认为合适的方式去做。
我正在尝试编写一个 TypeScript 类型,将一个类型的键元组转换为仅具有这些属性的类型。以下代码是我得到的最接近的代码:
type Primitive = string | number | boolean;
type ExtractProps<TObject, TKeys extends (keyof TObject)[], TExtends = any> = {
[Index in keyof TKeys as TKeys[Index] extends keyof TObject ? (TObject[TKeys[Index]] extends TExtends ? TKeys[Index] : never) : never ]:
TKeys[Index] extends keyof TObject ? TObject[TKeys[Index]] : never
};
type Type = {
'string': string;
'number': number;
'boolean': boolean;
'array': Array<any>
}
type Test1 = ExtractProps<Type, ['string', 'number', 'array']>;
// Output:
// type Test1 = {
// string: string | number | any[];
// number: string | number | any[];
// array: string | number | any[];
// }
// Desired Output:
// type Test1 = {
// string: string;
// number: number;
// array: any[];
// }
type Test2 = ExtractProps<Type, ['string', 'number', 'array'], Primitive>;
// Output:
// type Test2 = {
// string: string;
// number: number;
// }
// Desired Output:
// type Test2 = {
// string: string;
// number: number;
// }
type Test3 = ExtractProps<Type, ['string', 'number'], Primitive>;
// Output:
// type Test3 = {
// string: string | number;
// number: string | number;
// }
// Desired Output:
// type Test3 = {
// string: string;
// number: number;
// }
只有当重新映射表达式对其中一个键的计算结果为 never
时,我才能使该类型正常工作(如 Type2 中的 array
和 属性 类型一样不扩展 Primitive
)。有没有一种写法 ExtractProps
使得 Type1 和 Type3 有正确的 属性 类型?
你找的好像是the Pick<T, K>
utility type which produces a type from T
with only a subset of the known properties, specifically those whose keys are in K
, and something we can call PickByValue<T, V>
(as requested in T
with only a subset of the known properties, specifically those whose values are in V
. We can use key remapping via as
的组合写成PickByValue
:
type PickByValue<T, V> =
{ [K in keyof T as T[K] extends V ? K : never]: T[K] };
然后合并得到ExtractProps
:
type ExtractProps<T, K extends keyof T, V = any> =
PickByValue<Pick<T, K>, V>
我们来测试一下:
type Test1 = ExtractProps<Type, 'string' | 'number' | 'array'>;
/* type Test1 = {
string: string;
number: number;
array: any[];
} */
type Test2 = ExtractProps<Type, 'string' | 'number' | 'array', Primitive>;
/* type Test2 = {
string: string;
number: number;
} */
type Test3 = ExtractProps<Type, 'string' | 'number', Primitive>;
/* type Test3 = {
string: string;
number: number;
} */
看起来不错。
请注意,您也可以通过组合 Pick
和 PickByValue
的功能而不使用它们来直接实现 ExtractProps
:
type ExtractProps<T, K extends keyof T, V = any> =
{ [P in keyof T as T[P] extends V ? Extract<K, P> : never]: T[P]
}
最后,请注意,如问题中所写,您的 ExtractProps
版本希望其第二个参数是 arraylike 类型,其元素是键。你可以做
那:
type ExtractPropsArray<T, KS extends Array<keyof T>, V = any> =
PickByValue<Pick<T, KS[number]>, V>;
但实现不关心数组类型的细节,只关心其元素类型的并集。它只是丢弃了关于 KS
的大部分信息。我不建议编写一个需要它实际上不需要的信息的类型。你总是可以在使用数组之前解包它,比如 ExtractProps<T, KS[number], V>
,而当你从类似键的类型开始时发明一个无用的数组包装器有点愚蠢,比如 ExtractPropsArray<T, K[], V>
或 ExtractPropsArray<T, [K, K], V>
。但这最终更多的是一种意见,而不是权威来源,所以你应该按照你认为合适的方式去做。