gpt4 book ai didi

typescript - 联合类型 : PartialRequired and PickRequired

转载 作者:搜寻专家 更新时间:2023-10-30 21:25:32 26 4
gpt4 key购买 nike

今天我在我的项目中创建了两个新的 UnionType。

您可以在 Code-Review 中看到此类型: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 这样的新定义?

TypeScript Playground

编辑:

目标如下:

我有一些模型看起来像这样:

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 中查找查看 key 是否已映射,如果已映射,则将其映射...如果您这样做了 TMyModelM{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} 中.


好的,希望对你有帮助;祝你好运!

Link to code

关于typescript - 联合类型 : PartialRequired and PickRequired,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56876112/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com