递归排除Typescript中的只读属性
Recursively exclude readonly properties in Typescript
我很确定我缺乏使用复杂泛型的经验,所以我希望有人对如何实现这一点有想法。我的用例是为我的 React/Formik 表单值创建 "form types",而无需重新键入新定义或传入具有大量不可更新属性的完整对象。
我找到了这个答案,它展示了如何从 TypeScript 类型中排除只读属性,但我发现很难全神贯注地让它递归。此外,我希望它省略 return 空嵌套对象的属性(如果可能)。
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];
type Writable<T> = Pick<T, WritableKeys<T>>
type Brand = {
readonly id: number;
name: string;
logo: {
readonly id: number;
url: string;
},
creator: {
readonly id: number;
}
};
type EditableBrand = Writable<Brand>;
// type EditableBrand = {
// name: string;
// logo: {
// url: string;
// }
// };
有一个suggestion for a DeepReadonly<T>
type in TypeScript repo and some suggested implementations as well. And the same question on Whosebug. Here。是如何实现 DeepReadonly
的示例。您可以使用相同的技术来实现 DeepWritable
type:
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type DeepWritablePrimitive = undefined | null | boolean | string | number | Function;
type DeepWritable<T> =
T extends DeepWritablePrimitive ? T :
T extends Array<infer U> ? DeepWritableArray<U> :
T extends Map<infer K, infer V> ? DeepWritableMap<K, V> :
T extends Set<infer T> ? DeepWriableSet<T> : DeepWritableObject<T>;
type DeepWritableArray<T> = Array<DeepWritable<T>>;
type DeepWritableMap<K, V> = Map<K, DeepWritable<V>>;
type DeepWriableSet<T> = Set<DeepWritable<T>>;
type DeepWritableObject<T> = {
[K in WritableKeys<T>]: DeepWritable<T[K]>
};
然后让我们扩展类型以省略 return 清空嵌套对象的键:
...
type EmptyKeys<T> = {
[P in keyof T]: {} extends T[P] ? P : never
}[keyof T];
type OmitEmptyKeys<T> = Omit<T, EmptyKeys<T>>;
type DeepWritable<T> = ... : OmitEmptyKeys<DeepWritableObject<T>>;
我很确定我缺乏使用复杂泛型的经验,所以我希望有人对如何实现这一点有想法。我的用例是为我的 React/Formik 表单值创建 "form types",而无需重新键入新定义或传入具有大量不可更新属性的完整对象。
我找到了这个答案,它展示了如何从 TypeScript 类型中排除只读属性,但我发现很难全神贯注地让它递归。此外,我希望它省略 return 空嵌套对象的属性(如果可能)。
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];
type Writable<T> = Pick<T, WritableKeys<T>>
type Brand = {
readonly id: number;
name: string;
logo: {
readonly id: number;
url: string;
},
creator: {
readonly id: number;
}
};
type EditableBrand = Writable<Brand>;
// type EditableBrand = {
// name: string;
// logo: {
// url: string;
// }
// };
有一个suggestion for a DeepReadonly<T>
type in TypeScript repo and some suggested implementations as well. And the same question on Whosebug. Here。是如何实现 DeepReadonly
的示例。您可以使用相同的技术来实现 DeepWritable
type:
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type DeepWritablePrimitive = undefined | null | boolean | string | number | Function;
type DeepWritable<T> =
T extends DeepWritablePrimitive ? T :
T extends Array<infer U> ? DeepWritableArray<U> :
T extends Map<infer K, infer V> ? DeepWritableMap<K, V> :
T extends Set<infer T> ? DeepWriableSet<T> : DeepWritableObject<T>;
type DeepWritableArray<T> = Array<DeepWritable<T>>;
type DeepWritableMap<K, V> = Map<K, DeepWritable<V>>;
type DeepWriableSet<T> = Set<DeepWritable<T>>;
type DeepWritableObject<T> = {
[K in WritableKeys<T>]: DeepWritable<T[K]>
};
然后让我们扩展类型以省略 return 清空嵌套对象的键:
...
type EmptyKeys<T> = {
[P in keyof T]: {} extends T[P] ? P : never
}[keyof T];
type OmitEmptyKeys<T> = Omit<T, EmptyKeys<T>>;
type DeepWritable<T> = ... : OmitEmptyKeys<DeepWritableObject<T>>;