【问题标题】:Typescript mapped type where only nullable properties are retained and converted to string typeTypescript 映射类型,其中仅保留可为空的属性并转换为字符串类型
【发布时间】: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',
    },
}

【问题讨论】:

    标签: typescript nullable mapped-types


    【解决方案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。这基本上就是我所缺少的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 2019-03-10
    • 2019-10-28
    • 2020-01-07
    • 1970-01-01
    • 2019-02-09
    相关资源
    最近更新 更多