【问题标题】:typesafe extraction of a given set of fields from an object从对象中提取给定字段集的类型安全
【发布时间】:2021-01-04 09:07:02
【问题描述】:

我从一个 API 调用中得到一个 json 响应(稍后会变成一个对象),我可以使用 queryparams 动态限制某些字段。

我已经输入了完整的回复。

现在我想要实现的是创建一个类型,它接受我给定的字段并返回一个对象类型,它只包含给定的属性。

我知道有像 Pick 这样的实用程序类型,但它们不适用于嵌套对象。

示例:

interface ResponseInterface {
    a: {
        a: {
            a: number;
            b: string;
        };
        b: {
            a: string;
            b: boolean;
        };
    };
    b: {
        a: object | null;
        b: string;
    }[];
}


const filters = ['a.a.a', 'a.b.b', 'b.a']; //nested properties seperated by dots
const fakeFullResponse: ResponseInterface = { //calling the api without any filters
            a: {
                a: {
                    a: 123,
                    b: 'aab',
                },
                b: {
                    a: 'aba',
                    b: true,
                },
            },
            b: [
                {
                    a: null,
                    b: 'array bb',
                },
                {
                    a: {},
                    b: 'array bb',
                },
            ],
        };


interface ExpectedType { //after applying those filters to the object
    a: {
        a: {
            a: number;
        };
        b: {
            b: boolean;
        };
    };
    b: {
        a: null | object
    }[];
}

const fakeFilteredResponse: ExpectedType = { //calling the api with those fields above would get me this
            a: {
                a: {
                    a: 123,
                },
                b: {
                    b: true,
                },
            },
            b: [
                {
                    a: null,
                },
                {
                    a: {},
                },
            ],
        };

在给定ResponseInterface 接口和filters 的情况下,有没有办法动态创建ExpectedType 接口?如果需要,可以更改 filters 数组的格式,只要嵌套结构保持不变

【问题讨论】:

    标签: typescript typescript-typings mapped-types


    【解决方案1】:

    首先,与ExpectedType.b 相比,ResponseInterface.b 的定义毫无意义,因为前者是一个固定长度为 2 的元组,而后者是一个任意长度的数组(更新:OP 已修复这)。因此,为了有意义,请允许我假设您的意思如下:

    interface ResponseInterface {
        a: {
            a: {
                a: number;
                b: string;
            };
            b: {
                a: string;
                b: boolean;
            };
        };
        b: {
            a: null | object;
            b: string;
        }[];
    }
    

    接下来,如果过滤器是以字符串的形式编写的,那么您将需要 TypeScript 来解析您的字符串字面量类型,该功能仅存在于即将发布的 4.1 版本中。即使有这种能力,重新排列类型结构也困难得多,因为您要逐个列出过滤器而不是按结构列出。

    因此,我建议您将过滤器格式化为一种类型:

    type filters = { 'a': { 'a': 'a', 'b': 'b' }, 'b': 'a' };
    

    格式应该是不言自明的。然后您可以使用以下实用程序:

    type PickStructure<T, F> = F extends keyof T ? { [k in F]: T[k] } : {
        [k in keyof F]: k extends keyof T ? (
            T[k] extends (infer U)[] ? PickStructure<U, F[k]>[] : PickStructure<T[k], F[k]>
        ) : never;
    };
    
    type ExpectedType = PickStructure<ResponseInterface, filters>;
    

    而且它有效。看到这个Playground Link

    【讨论】:

    • 是的,没错,我在ResponseInterface.b 上复制意大利面失败。就像一个魅力,非常感谢你
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多