Typescript 使用条件类型编写 RequiredProps 泛型
Typescript writing RequiredProps generic using conditional types
问题
我正在尝试定义一个通用 RequiredProps<T, K>
,以便我可以要求接口 T
上的键 K
不可为空。
例如:
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
要将对象类型的交集转换为单一类型,您可以在其属性上使用标识 mapping:
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>>
的一句话。从概念上讲,这与您所做的类似,只是我使用的是 standard-library-provided utility types.
但还是有区别的;您的 {[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
未提及的 required-ness 属性不会更改。
总之,希望对您有所帮助;祝你好运!
问题
我正在尝试定义一个通用 RequiredProps<T, K>
,以便我可以要求接口 T
上的键 K
不可为空。
例如:
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
要将对象类型的交集转换为单一类型,您可以在其属性上使用标识 mapping:
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>>
的一句话。从概念上讲,这与您所做的类似,只是我使用的是 standard-library-provided utility types.
但还是有区别的;您的 {[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
未提及的 required-ness 属性不会更改。
总之,希望对您有所帮助;祝你好运!