最近,我在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.
任何想法如何让这个工作为数组?
答案 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]>;
};