我需要编写一个函数,通过向对象树中的每个 node 添加访问控制字段$
来为对象生成注释.
要注释对象{ field: {...subFields} }
,我们需要获取{ field: { $: 'view', ...subFieldsAnnotated } }
.
要注释数组及其元素,只要有类似{ arr: [...elements] }
的内容,我们就应该用{ arr: { $: 'view', $items: [...elementsAnnotated] } }
替换它.
我想做以下工作:
type Annotated<T, NewFieldName extends string, NewFieldValue> =
T extends Array<infer U>
? // Wrap arrays in an object with '$items' and the new field.
{
$items: Array<Annotated<U, NewFieldName, NewFieldValue>>;
} & {
[K in NewFieldName]: NewFieldValue;
}
: // Check if T is an object (but not an array, which is handled above).
T extends object
? {
[P in keyof T]: Annotated<T[P], NewFieldName, NewFieldValue>;
} & { [K in NewFieldName]: NewFieldValue }
: // Directly create an object with the new field for primitives.
{ [K in NewFieldName]: NewFieldValue };
type Access = "none" | "view" | "edit";
type Accessor<T> = Annotated<T, "$", Access>;
function isObject<T>(value: T): boolean {
return value !== null && typeof value === "object";
}
// this is okay
const emptyObjAnnotated: Accessor<{}> = {
$: "view",
};
// this in not
function getAccessor<T>(value: T, access: Access): Accessor<T> {
if (isObject(value)) {
if (!Array.isArray(value)) {
// >> ERROR: should extend T with '{}' or 'object'
const keys = Object.keys(value) as (keyof typeof value)[];
// >> ERROR: Type '{ $: Access; }' is not assignable to type 'Annotated<T, "$", Access>'
return {
$: access,
...keys.reduce((acc, key) => {
if (isObject(value[key])) {
return {
...acc,
[key]: getAccessor(value[key], access),
};
}
return {
...acc,
[key]: { $: access },
};
}, {}),
};
}
if (Array.isArray(value)) {
// >> ERROR: Type '{ $: Access; $items: { $: Access; }[]; }' is not assignable to type 'Annotated<T, "$", Access>'
return {
$: access,
$items: value.map((child) => {
if (isObject(child)) {
return getAccessor(child, access);
}
return { $: access };
}),
};
}
}
// ERROR: Type '{ $: Access; }' is not assignable to type 'Annotated<T, "$", Access>'
return { $: access };
}
// // for testing purposes
//
// type ExampleType = {
// id: number;
// name: string;
// tags: string[];
// details: {
// description: string;
// images: {
// url: string;
// alt: string;
// }[];
// };
// };
//
// const example: ExampleType = {
// id: 1,
// name: "name",
// tags: ["tag1", "tag2"],
// details: {
// description: "description",
// images: [
// {
// url: "url",
// alt: "alt",
// },
// ],
// },
// };
//
// const access = getAccessor(example, "view");
//
// console.log(access.id.$); // 'view'
// console.log(access.details.$); // 'view'
// console.log(access.details.images.$items[0].url.$); // 'view'
问题似乎是,当在子类上递归时,类型泛型Annotated
的当前版本不接受类型{ [K in NewFieldName]: NewFieldValue; }
.
算法本身似乎是正确的,所以当我将函数的签名更改为
function getAccessor(obj: any, access: Access): Accessor<typeof obj>
// .....
它给出了预期的结果,但是,当然,我会失go 所有的类型表示法,所以我将无法引用基于Accessor<T>
的数据 struct 的字段.
你有什么建议吗?我怎么才能打捞到这些人呢?