I have this basically (a small snippet of the types I have so far, which is far from complete yet):
export type ASTFormType = {
base: Array<ASTFormType>;
hook: Record<string, ASTHookType>;
like: "form";
link: Record<string, ASTTakeType>;
name: string;
task: Record<string, ASTTaskType>;
wear: Record<string, ASTWearType>;
};
export type ASTHookType = {
flow: Array<ASTTaskFlowType>;
like: "hook";
name: string;
take: Record<string, ASTTakeType>;
};
export type ASTTaskCallType = {
bind: Record<string, ASTBindType>;
like: "call";
};
export type ASTHoldType = {
like: "hold";
};
export type ASTTakeType = {
like: "take";
name: string;
takeLike: ASTLikeType;
};
export type ASTLikeType = {
bind?: Record<string, ASTLikeType>;
like: "like";
name: string;
};
export type ASTTaskFlowType = ASTTaskCallType | ASTHoldType;
export type ASTBindType = {
bond: ASTBondType;
like: "bind";
name: string;
};
export type ASTBondType = ASTCordType | ASTMarkType;
export type ASTCordType = {
cord: string;
like: "cord";
};
export type ASTMarkType = {
like: "mark";
mark: number;
};
export type ASTWearType = {
like: "wear";
link: Record<string, ASTTakeType>;
name: string;
task: Record<string, ASTTaskType>;
};
export type ASTTaskType = {
like: "task";
move: Array<ASTTaskFlowType>;
take: Record<string, ASTTakeType>;
task: Record<string, ASTTaskType>;
};
The type ASTFormType
has a bunch of nested objects, which can have nested objects / arrays of nested objects, etc.. So you could have:
const formA = {
like: 'form',
name: 'user',
link: {
email: {
name: 'email',
takeLike: {
name: 'string'
}
},
username: {
name: 'username',
takeLike: {
name: 'string'
}
}
},
task: {
login: { ... }
}
}
But, in this compiler, when this object is first constructed, it is hardly filled out. Instead of being filled out, it looks essentially like this (this is what I currently have):
export type NestedPartial<T extends object> = {
[K in keyof T]?: T[K] extends object ? NestedPartial<T[K]> : T[K];
};
const form: NestedPartial<ASTFormType> = {
base: [],
hook: {},
like: "form",
link: {},
// name: "", // leave off for demo purposes
task: {},
// wear: {}, // leave this off for demo purposes
};
With that NestedPartial
, I am trying to say "this form
record itself is partial", meaning it doesn't have all it's properties necessarily. And also want to say, all the nested maps and arrays have objects which are also partial! So something like this (which doesn't work):
const form: Partial<ASTFormType> = {
base: <Partial<ASTLikeType>>[],
hook: <Record<string, Partial<ASTHookType>>>{},
like: "form",
link: <Record<string, Partial<ASTTakeType>>>{},
task: <Record<string, Partial<ASTTaskType>>>{},
};
But (a), that is a lot of annotations, and (b) it doesn't actually work (syntax error).
How can I do something like this though? Partially typing the tree of objects. I don't want to say (recursively) that everything is Partial
(like my NestedPartial
tries to do (but fails at for some reason)). I want to just say that specific pieces of the tree of objects are Partial
. How can I accomplish something like that?
That way, I can progressively build up the objects and some attributes can be undefined while I am in the middle of processing. Only later will I strip out the Partial
from the definition, to get back to the inner object fully filled out!