首页 > 解决方案 > 在打字稿中实现安全导航

问题描述

最近,我在 Medium 中看到了Gidi Meir Morris的关于使用 ES6 的代理进行安全对象属性访问的以下文章。我真的很喜欢它,并想在我的 Typescript 项目中尝试一下可选嵌套对象,而不会丢失类型检查。

为了将可选的嵌套对象变成所有必需的,我使用以下类型:

export type DeepRequired<T> = {
    [P in keyof T]-?: DeepRequired<T[P]>;
};

Gidi 在 typescript 中的代码(包括一些技巧......):

export interface Dictionary {
    [key: string]: any;
};

const isObject = (obj: any) => obj && typeof obj === 'object';
const hasKey = (obj: object, key: string) => key in obj;

const Undefined: object = new Proxy({}, {
    get: function (target, name) {
        return Undefined;
    }
});

export const either = (val: any, fallback: any) => (val === Undefined ? fallback : val);

export function safe<T extends Dictionary>(obj: T): DeepRequired<T> {
    return new Proxy(obj, {
    get: function(target, name){
        return hasKey(target, name as string) ? 
        (isObject(target[name]) ? safe(target[name]) : target[name]) : Undefined;
    }
    }) as DeepRequired<T>;
}

使用示例:

interface A {
    a?: {
        b?: {
            c?: {
                d?: string
            }
        }
    },
    b: boolean,
    c?: {
        d: {
            e: number
        }
    },
    d?: Array<{e: boolean}> 
}
const obj: A = {b: false};
const saferObj = safe(obj);

它在没有 TS 错误的情况下工作的场景:

test('should work for nested optional objects', () => {
    expect(either(saferObj.a.b.c.d, null)).toEqual(null);
    expect(either(saferObj.a.b.c.d, undefined)).toEqual(undefined);
    expect(either(saferObj.a.b.c.d, 322)).toEqual(322);
});

test('should work for required members', () => {
    expect(either(saferObj.b, null)).toEqual(false);
});

test('should work for mixed optional/required tree', () => {
    expect(either(saferObj.c.d.e, null)).toEqual(null);
});

至于数组...

test('should work for arrays', () => {
    expect(either(saferObj.d[0].e, null)).toEqual(null);
});

TS 编译器抛出以下错误:

[ts] Element implicitly has an 'any' type because type 'DeepRequired<{ e: boolean; }[]>' has no index signature.

知道我怎样才能使这个适用于数组吗?

标签: javascripttypescript

解决方案


您的代码将在 Typescript 2.9 及更高版本上正常工作,因为在 Typescript 2.9 中,keyof运算符包括数字和符号键以及之前由keyof. 游乐场链接

如果您出于某些原因想要坚持使用 2.8,您可以使用在DeepRequired使用条件类型时显式处理数组的解决方法。

export type DeepRequired<T> = {
    [P in keyof T]-?: T[P] extends Array<infer U>?Array<DeepRequired<U>>: DeepRequired<T[P]>;
};

推荐阅读