Допустим, я создаю минимальный компилятор в TypeScript:
У меня есть парсеры, которые применяются к различным типам файлов:
interface Parser<ParsedTree extends object> {
parse(buffer: Buffer): ParsedTree;
}
У меня есть преобразования, которые могут изменяться или не изменятьсяпосещенные узлы деревьев проанализированных файлов:
interface Transform<Node, TransformedNode> {
pre(node: Node): TransformedNode;
post(node: Node): TransformedNode;
}
И у меня есть цели, которые принимают преобразованные деревья и что-то с ними делают (якобы записывают их в фс):
interface Target<TransformedTree> {
write(tree: TransformedTree): void;
}
Тип цели TransformedTree
должен быть взят из того, что возвращают преобразования. Ака., Мы получаем возвращаемый тип объединения для всех преобразований 'pre
и post
и добавляем его в анализируемые типы узлов дерева. Для этого мы получаем возвращаемые типы синтаксического анализатора, а затем используем следующий тип утилиты, чтобы превратить каждый из узлов типа дерева в объединение самого себя, а затем возвращаем типы всех преобразований: методы pre
и post
:
export type ChildrenUnionWith<T, Append> = {
[K in keyof T]: T[K] extends object
? ChildrenUnionWith<T[K], Append> | Append
: T[K] | Append;
};
Между тем, мы получаем первый универсальный аргумент Transform
, используя следующую утилиту, чтобы получить объединение всех узлов ParsedTree
:
export type NodeOfTree<Tree> = Tree extends object
? ({[K in keyof Tree]: NodeOfTree<Tree[K]>}[keyof Tree] | Tree)
: Tree;
Я пыталсяобъединить эти типы следующим образом:
function compile<
Parsers extends Parser<object>[],
Transforms extends Transform<NodeOfTree<ParsedTree>, unknown>[],
Targets extends Target<TransformedTree>[],
ParsedTree = ReturnType<Parsers[number]["parse"]>,
TransformedNode = ReturnType<Transforms[number]["pre" | "post"]>,
TransformedTree = ChildrenUnionWith<ParsedTree, TransformedNode>
>(parsers: Parsers, transforms: Transforms, targets: Targets) {
// do something
}
Это работает ... но при использовании во время выполнения данное Transform
может действовать на узел, который был ранее преобразован. Из-за этого я хотел бы передать возвращаемые типы всех преобразований обратно в свой первый универсальный аргумент ... Я хотел бы, чтобы тип узла был - не только объединение всех ParsedTree
узлов, но такжеобъединение всех типов возврата Transform
pre
и post
.
Следующее не работает должным образом:
function compile<
Parsers extends Parser<object>[],
Transforms extends Transform<NodeOfTree<ParsedTree> | TransformedNode, unknown>[],
Targets extends Target<TransformedTree>[],
ParsedTree = ReturnType<Parsers[number]["parse"]>,
TransformedNode = ReturnType<Transforms[number]["pre" | "post"]>,
TransformedTree = ChildrenUnionWith<ParsedTree, TransformedNode>
>(parsers: Parsers, transforms: Transforms, targets: Targets) {
// do something
}
Когда я передаю следующие параметры ...
compile(
[{parse: (_: Buffer) => ({a: "b", c: "d"})}],
[
{
pre: (node) => {},
post: (node) => {},
},
],
[{write: (node) => {}}],
);
... не только нет ошибок типа, но node
тип pre
и post
равен any
: /
У кого-нибудь есть какие-либо предложенияо том, как я мог бы разрешить ввод, который я ищу?
Спасибо!