在打字稿中实现安全导航

时间:2018-06-07 09:01:22

标签: javascript typescript

最近,我在Gidi Meir Morris的媒体关于Using ES6's Proxy for safe Object property access的文章中看到了以下文章。我真的很喜欢它,想在我的Typescript项目中试一试可选的嵌套对象,而不会丢失类型检查。

为了将可选的嵌套对象转换为all-required,我使用以下类型:

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

Gidi的打字稿代码(包括一些黑客......):

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.

任何想法如何让这个工作为数组?

1 个答案:

答案 0 :(得分:1)

您的代码将在Typescript 2.9及更高版本上工作,因为在Typescript 2.9中,keyof运算符包括数字和符号键以及先前由keyof返回的字符串键。 Playground link

如果由于某些原因想要坚持使用2.8,可以使用条件类型在DeepRequired中显式处理数组的变通方法。

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