Typescript 映射类型,其中仅保留可为空的属性并转换为字符串类型
Posted
技术标签:
【中文标题】Typescript 映射类型,其中仅保留可为空的属性并转换为字符串类型【英文标题】:Typescript mapped type where only nullable properties are retained and converted to string type 【发布时间】:2021-10-13 06:08:54 【问题描述】:我正在尝试从现有类型创建新的映射类型。我希望用 string
类型替换所有可为空的属性。基本思想是让映射类型用string
类型替换子记录中的所有可为空的属性类型。如果子记录不包含任何可为空的类型,则子记录本身将从映射类型中排除。此外,任何不是对象的属性都被排除在外。
interface OriginalType
foo:
bar: string | null;
baz: number | null;
;
bar:
qux: string;
quz: boolean | null;
wobble: string | null;
;
baz:
grault: string;
garply: number;
flob: boolean;
;
version: number;
interface ExpectedType
foo:
bar: string;
baz: string;
;
bar:
quz: string;
wobble: string;
;
目前为止我已经写了这个映射类型:
type RetainNullablesAsString<T> =
[C in keyof T]: T[C] extends object ?
[K in keyof T[C]]: null extends T[C][K] ? string : never;
: never;
如果测试它我得到一个错误:
类型 ' quz: string; 中缺少属性 'qux'摆动:字符串; ' 但在 ' qux: never; 类型中是必需的quz:字符串;摆动:字符串; '。
我打算将 qux
属性从类型中排除,因为它不可为空,但我不知道该怎么做 - 我目前在映射类型中将其设置为 never
。我认为 Pick 实用程序类型在这里没有帮助,因为我不想将任何属性硬编码到映射类型。还应排除 baz
记录,因为它的所有子属性都不能为空。此外,我不知道是否可以创建这样的映射类型,但如果可以的话我会很高兴。
interface OriginalType
foo:
bar: string | null;
baz: number | null;
;
bar:
qux: string;
quz: boolean | null;
wobble: string | null;
;
baz:
grault: string;
garply: number;
flob: boolean;
;
version: number;
const sourceRecord: OriginalType =
foo:
bar: 'bar',
baz: 0,
,
bar:
qux: 'qux',
quz: false,
wobble: null,
,
baz:
grault: 'grault',
garply: 1,
flob: true,
,
version: 1,
//interface ExpectedType
// foo:
// bar: string;
// baz: string;
// ;
// bar:
// quz: string;
// wobble: string;
// ;
//
type RetainNullablesAsString<T> =
[C in keyof T]: T[C] extends object ?
[K in keyof T[C]]: null extends T[C][K] ? string : never;
: never;
const sourceRecordMetaData: RetainNullablesAsString<OriginalType> =
foo:
bar: 'bar-meta',
baz: 'baz-meta',
,
bar:
quz: 'quz-meta',
wobble: 'wobble-meta',
,
【问题讨论】:
【参考方案1】:interface OriginalType
foo:
bar: string | null;
baz: number | null;
;
bar:
qux: string;
quz: boolean | null;
wobble: string | null;
;
baz:
grault: string;
garply: number;
flob: boolean;
;
version: number;
type IsNullableProperty<K extends keyof T, T> = null extends T[K] ? K : never;
// Filtering out non nullable properties and convert rest to strings
type ConvertSubRecord<T> =
[K in keyof T as IsNullableProperty<K, T>]: string;
;
type IsEmptyObject<T> = keyof T extends never ? never : T;
// It transform property keys for non records and records that are converted into empty objects into never
type IsNotEmptySubRecord<K extends keyof T, T> = T[K] extends object
? keyof ConvertSubRecord<T[K]> extends never
? never
: K
: never;
type Convert<T> =
// Filter out with IsNotEmptySubRecord helper and convert subrecords with ConvertSubRecord
[K in keyof T as IsNotEmptySubRecord<K, T>]: ConvertSubRecord<T[K]>;
;
interface ExpectedType
foo:
bar: string;
baz: string;
;
bar:
quz: string;
wobble: string;
;
const res: Convert<OriginalType> =
bar:
quz: "",
wobble: ""
,
foo:
bar: "",
baz: "",
,
【讨论】:
不错!我不知道您可以将记录键设置为never
。这基本上就是我所缺少的。以上是关于Typescript 映射类型,其中仅保留可为空的属性并转换为字符串类型的主要内容,如果未能解决你的问题,请参考以下文章