今天我在项目中创建了两个新的 UnionType。
您可以在代码审查中看到此类型:https://codereview.stackexchange.com/questions/223433/uniontypes-partialrequired-and-pickrequired
export type PartialRequired<T, K extends keyof T> = Partial<T> & Pick<Required<T>, K>;
export type PickRequired<T, K extends keyof T> = T & Pick<Required<T>, K>;
export type ForceId<T extends { id?: ID }, ID = number> = PickRequired<T, 'id'>;
现在我想使 id
属性可重命名?!
我尝试过这样的事情:
export type ForceId<T extends { [key: ID_NAME]?: ID }, ID = number, ID_NAME extends string = 'id'> = PickRequired<T, ID_NAME>;
// ~~ ';' expected
但是正如您所看到的,这不起作用。有什么方法可以实现这样的目标吗?
所以我可以使用 ForceId<MyModel, string, 'uuid'>
=> { uuid: '123e4567-e89b-12d3-a456-426655440000' }
而无需创建像 ForceUuid
这样的新定义?
编辑:
目标如下:
我有一些模型看起来像这样:
interface MyModel1 {
id?: number; // The identifier of Model 1
name: string;
age?: number;
alive?: boolean;
}
interface MyModel2 {
uuid?: string; // The identifier of Model 2
name: string;
age?: number;
alive?: boolean;
}
我不想在运行时更改任何代码。
现在我想使用 ForceId
类型。
// Works
const model1: ForceId<MyModel1> = {
id: 0,
name: "test",
age: 10
};
// Don't work
const model2: ForceId<MyModel2, string, "uuid"> = {
uuid: "123e4567-e89b-12d3-a456-426655440000",
name: "test",
age: 10
};
最佳答案
我将回答有关如何以编程方式修改对象类型以重命名键的一般问题。
这是一种可能的实现,它仅适用于具有已知文字键的必需的可变属性(没有可选属性,没有 readonly
属性,也没有索引签名)。如果您需要支持这些情况,这是可能的,但会变得更加难看,所以我现在忽略它。
type ValueOf<T> = T[keyof T];
type RenameKeys<T, M extends Record<keyof any, keyof any>> = ValueOf<
{ [K in keyof T]: (x: Record<K extends keyof M ? M[K] : K, T[K]>) => void }
> extends ((x: infer R) => void)
? { [K in keyof R]: R[K] }
: never;
这可能会令人困惑,但基本上它需要一个对象类型 T
和键映射类型 M
,并生成一个新类型,其中键已重命名,但属性类型相同。例如:
interface MyModel {
id: number;
name: string;
age: number;
alive: boolean;
}
type Renamed = RenameKeys<MyModel, { id: "uuid" }>;
产生
type Renamed = {
uuid: number;
name: string;
age: number;
alive: boolean;
}
和
type RenamedMulti = RenameKeys<
MyModel,
{ id: "uuid"; alive: "notDead"; age: "yearsOld" }
>;
产生
type RenamedMulti = {
uuid: number;
name: string;
yearsOld: number;
notDead: boolean;
}
您也许可以使用RenameKeys
建立您正在寻找的类型。
至于它是如何工作的:
type RenameKeys<T, M extends Record<keyof any, keyof any>> = ValueOf<
{ [K in keyof T]: (x: Record<K extends keyof M ? M[K] : K, T[K]>) => void }
> extends ((x: infer R) => void)
? { [K in keyof R]: R[K] }
: never;
M
映射扩展Record<keyof any, keyof any>
只是确保它是从键到键的映射。然后,让我们想象一下:{[K in keyof T]: Record<K extends keyof M ? M[K] : K, T[K]>]}
。这基本上占据了 T
中的所有属性并查找 M
查看该键是否已映射,如果是,则映射它...如果您这样做了,则 T
是MyModel
和M
是{id: "uuid"}
,那么你会得到 {id: {uuid: number}, name: {name: string}, age: {age: number}, alive: {alive: boolean}}
。
从这种类型转换为具有这些组合的类型有点棘手...我的方法是使用 conditional type inference将这些类型的并集转换为交集。首先,我将这些属性放入函数参数中,例如 (x: Record<...> => void)
。然后我得到函数的并集(即 ValueOf<>
应用程序)并推断出参数类型为 R
的单个函数。从中。这最终成为一个交叉点,因为 (x: A)=>void | (x: B)=>void
可分配给(x: A & B) => void
。 (其工作原理的另一个解释是 here 或可能 here )
所以类型 R
结果看起来像 {uuid: number} & {name: string} & {age: number} & {alive: boolean}
,然后 {[K in keyof R]: R[K]}
是一个“身份”映射类型,它将这些属性收集到 {uuid: number; name: string; age: number; alive: boolean}
中.
好的,希望有帮助;祝你好运!
关于typescript - 联合类型 : PartialRequired and PickRequired,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56876112/