Typescript映射类型,其中只保留可空属性并转换为字符串类型



我正在尝试从现有类型创建新的映射类型。我希望用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;
}

如果测试它,我得到一个错误:

属性'qux'在类型'{quz: string;摆动:字符串;}',但在类型'{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',
},
}

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: "",
},
}

最新更新