使用条件类型编写泛型的RequiredProps打字稿

问题描述 投票:0回答:1

问题

我正在尝试定义一个通用的

RequiredProps<T, K>
,以便我可以要求接口
K
上的键
T
不可为空。

例如:

interface User {
  id: string;
  phoneNumber?: string;
}
type UserWithPhoneNumber = RequiredProps<User, 'phoneNumber'>

应该产生一个类型

UserWithPhoneNumber
相当于
{ id: string; phoneNumber: string }

解决方案

经过一些尝试和错误,我得到了一些有用的东西:

type RequiredProps<T, K extends keyof T> = {
  [P in Exclude<keyof T, K>]: T[P];
} & {
  [P in K]-?: T[P];
};

这会产生等效类型

{ name: string } & { phoneNumber: string }
,但是为了可读性,我希望它完全是
{ id: string; phoneNumber: string }

查看

lib.es5.d.ts
中的其他类型,我以为我可以使用条件类型修复它,但是以下方法不起作用:

type RequiredProps<T, K extends keyof T> = {
  [P in keyof T]: P extends K ? NonNullable<T[P]> : T[P];
};

为什么这不起作用,如何修复?

这是一个可以修改的 CodeSandbox: https://codesandbox.io/s/serene-meadow-j20w1?file=/src/index.ts

typescript typescript-generics conditional-types
1个回答
1
投票

要将对象类型的交集转换为单一类型,您可以在其属性上使用恒等映射

type Id<T> = { [K in keyof T]: T[K] };

您可以看到它的行为方式:

type Test = Id<{ a: string } & { b: number } & { c: boolean }>;
/* type Test = {
    a: string;
    b: number;
    c: boolean;
} */

然后你可以像这样创建你的

RequiredProps
类型别名:

type RequiredProps<T, K extends keyof T> = 
  Id<Omit<T, K> & Required<Pick<T, K>>>;

让我们验证一下它是否能满足您的需求:

type UserWithPhoneNumber = RequiredProps<User, 'phoneNumber'>;
/* type UserWithPhoneNumber = {
    id: string;
    phoneNumber: string;
} */

看起来不错。 与传入的属性相比,出现的属性可能会被重新排序,这根本不会影响类型兼容性。 希望这没问题,因为我想不出什么可以确保它们以相同的顺序出现。


关于

Omit<T, K> & Required<Pick<T, K>>
的一句话。 从概念上讲,这与您所做的类似,只是我使用的是标准库提供的实用程序类型

但是有一个区别:您的

{[P in Exclude<keyof T, K>]: T[P]}
不再是 同态,因此不会保留
T
的属性修饰符。 有关同态映射类型如何保留属性修饰符的说明,请参阅 microsoft/TypeScript#12563。 所以如果你这样做:

type U1 = RequiredPropsV1<User, "id">
/* type U1 = {
    phoneNumber: string | undefined;
} & {
    id: string;
} */
const u1: U1 = { id: "" }; // error! phoneNumber is required

您将看到您的版本最终实现了所需的所有属性(但将

undefined
留在未提及的属性域中)。想必您想要的东西更像是:

type U2 = RequiredProps<User, "id">
/* type U2 = {
    phoneNumber?: string | undefined;
    id: string;
} */
const u2: U2 = { id: "" }; // okay

未提及的属性的必需性不会改变。


游乐场链接

© www.soinside.com 2019 - 2024. All rights reserved.