问题描述
问题
我试图定义一个通用的sales_date
,以便我可以要求接口annual_unqiue_count <> 0
上的键SELECT
customerID,MONTH(sales_date),SUM(annual_unqiue_count),SUM(sales_volume),(CASE WHEN annual_unqiue_count <> 0
THEN SUM(sales_volume) over (PARTITION BY customerId)
ELSE 0 END) AS sales_volume
FROM sales
GROUP BY 1,2;
不可为空。
例如:
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 }
为什么不起作用,如何解决?
这里是一个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>>
的字词。从概念上讲,这与您的操作类似,不同之处在于,我使用的是标准库提供的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
未提及的属性的必需性不会改变。
无论如何,希望能有所帮助;祝你好运!