Может ли TypeScript Compiler API разрешить значение выражения?

Мне интересно, есть ли способ разрешить окончательное значение для выражения (с использованием API компилятора TypeScript), которое будет работать аналогично getTypeAtLocation.

Возьмем следующий пример:


const g = {
    a: 'hello'
}

export enum wonkyEnum {
  A, // = 0
  B = 1,
  C = g['a'] as any, 
  D = "str" as any
  E = [ ...[1,2,3] ][2]
}
  • В этом примере C, D и E не будут работать с getConstantValue ().
  • D легко проанализировать, однако C и E являются примерами более глубоких проблем из-за используемых выражений, ссылок и т. Д.

Я надеюсь, что существует даже частичное решение, которое не потребует написания задействованного рекурсивного парсера, охватывающего все возможные непредвиденные обстоятельства для выражения инициализатора. Если в TS ничего не отображается, может быть полезен сторонний код с открытым исходным кодом.

В противном случае поможет любая деталь, которая поможет мне узнать все возможные варианты узлов в этом месте!

У меня уже есть надежная система рекурсивного синтаксического анализатора, которую я использую в основном для прохождения структуры Type, но ее также можно реализовать для Node / Kind. В основном я хотел бы быть уверен, что не загромождаю код без надобности и не изобретаю велосипед перед тем, как пойти по этому пути.

Спасибо!

Редактировать

Ответ Дэвида, приведенный ниже, правильный - нет возможности сделать это с помощью TS.

При дальнейшем изучении, чтобы охватить все случаи, потребуется фактическая оценка. Это может стать беспорядочным и во многих случаях невозможным (см. Комментарии ниже).

К счастью, в случае перечислений TypeScript обычно может получить ответ с помощью метода getConstantValue ().

В качестве запасного варианта мы рекурсивно проанализируем узел инициализатора, охватывающий несколько случаев выражения (как показано ниже):

// key is tested for node.kind equality in parseInitializer()
const valueParsers = Map<number | number[], (node: any) => string | number | boolean | null>([
  [SyntaxKind.TrueKeyword, () => true],
  [SyntaxKind.FalseKeyword, () => false],
  [SyntaxKind.NullKeyword, () => null],
  [SyntaxKind.StringLiteral, (node: LiteralLikeNode) => node.text],
  [
    [SyntaxKind.AsExpression, SyntaxKind.ParenthesizedExpression, SyntaxKind.TypeAssertionExpression],
    (node: any) => parseInitializer(node.expression) // recursive call
  ],
]);

Если у кого-то есть идеи, как немного расширить его, не слишком сумасшедший, не стесняйтесь комментировать ниже!


person Ron S.    schedule 10.12.2019    source источник
comment
Не уверен, зачем вам нужен синтаксический анализ, если у вас уже есть абстрактное синтаксическое дерево.   -  person kaya3    schedule 11.12.2019
comment
Похоже, вы столкнетесь с оценкой произвольного кода JavaScript ... Итак ... получить выданный текст и просто вызвать eval?   -  person Gerrit0    schedule 11.12.2019
comment
@ Gerrit0, который будет работать, но они, вероятно, захотят запустить быструю проверку узлов, чтобы убедиться, что все потомки автономны (например, работает для [1, 2, 3][2], но не _2 _..., который может случайно сработать, если эти переменные были определены в исполняемый код)   -  person David Sherret    schedule 11.12.2019
comment
@ kaya3 Я имел в виду синтаксический анализ в смысле обхода дерева в поисках окончательного значения.   -  person Ron S.    schedule 11.12.2019
comment
@ Gerrit0 Думаю, ты прав. То же самое только что пришло мне в голову. Было бы разумно получить символы и вызвать eval, но тогда есть опасения, что что-то оцениваемое очень дорого, записывается на диск и т. Д. Или может потребоваться соединение с БД и т. Д., Поэтому это было бы невозможно во время AST парсинг для определения.   -  person Ron S.    schedule 11.12.2019


Ответы (1)


Это полезно, но компилятор не имеет встроенных функций для разрешения значений выражений. Это не касается этого ... getConstantValue в средстве проверки типов специально для членов перечисления.

Я также не верю, что на данный момент есть какая-либо библиотека, которая могла бы помочь в этом ... по крайней мере, я ее не видел. Возможно, вы или кто-то другой захотите его создать? Это был бы забавный проект!

В любом случае, это всего лишь мои высокоуровневые мысли, которые могут сработать, но их можно улучшить:

Определение значения присвоения собственности

Это довольно сложно сделать, потому что вам нужно будет проверить, не изменилось ли что-то g.a, прежде чем это будет назначено члену перечисления:

const g = { a: 'hello' };
g.a = 'other string';

Если вы действительно хотите реализовать что-то подобное, я считаю, что один из способов сделать это - получить символ g.a (getSymbolAtLocation), а затем посмотреть на назначение свойства в свойстве #valueDeclaration. Из этого назначения свойства вам нужно будет пройти узлы в дереве вниз, чтобы получить последнее назначение этому свойству перед объявлением члена перечисления (кто-то, ищущий проблемы, может переназначить свойство в предыдущем инициализаторе члена перечисления). Если выражение присваивания является константой или некоторым выражением, которое вы можете разрешить, тогда вы знаете значение. Если это не так, вам придется перейти к этому объявлению и оттуда пройти по дереву. По сути, это становится действительно сложным, и вы не всегда сможете определить значение статически.

Возможно, ограничением вашего анализатора может быть то, что строковые типы недопустимы, и в этом случае следует использовать утверждение const?

const g = { a: 'hello' } as const;

В этом случае getTypeAtLocation для a в g.a - это строковый литерал "hello".

Вложенные массивы

Для этих вложенных массивов [ ...[1,2,3] ][2] вы можете пройти по дереву в порядке публикации, разрешить элементы массива, сгладить массив из-за синтаксиса распространения, а затем получить третий элемент в массиве. Возможно, зная, что он ищет только третий элемент, вы могли бы его оптимизировать и разрешить только достаточно, чтобы получить второй элемент (это может сэкономить много времени, если массив имеет что-то вроде [...[g.a, 2, 3]]).

person David Sherret    schedule 11.12.2019
comment
Спасибо за то, что поделились своими мыслями! Оказывается, спред может разрешиться, а TS предоставляет разумные ограничения, если не указано «как есть», поэтому меня это не беспокоит. Пользователи намеренно нарушили бы стандартные ограничения перечисления с помощью любых. Одна вещь, которая пришла мне в голову, заключается в том, что будут сценарии, которые будут просто упущены даже при наилучшем синтаксическом анализе узлов. Например: E = myObj [(() = ›Object.keys (myObj) .filter (k =› k === 'myKey')) ()] - предположительно надуманный сценарий, но он заставляет меня понять, что оценка потребуется во многих сценариях. - person Ron S.; 11.12.2019
comment
Ах да, есть все виды дел, которые нужно рассмотреть, и во многих случаях это невозможно узнать. Вы уверены, что [ ...[1,2,3] ][2] работает? Я получаю undefined, когда пытаюсь передать этот член перечисления в getConstantValue, и я не вижу ничего в коде компилятора для обработки этого, хотя я, возможно, недостаточно внимательно смотрю. - person David Sherret; 11.12.2019
comment
Изменить: Забыть. Я ошибся значением! Ты прав. - person Ron S.; 11.12.2019
comment
Я считаю, что если вы сделаете перечисление const enum в редакторе, тогда оно должно показать все сценарии, в которых работает getConstantValue (без ошибок компиляции для этих членов). - person David Sherret; 11.12.2019
comment
Это отличная идея. Спасибо. Примером использования здесь является библиотека, которая анализирует типы и генерирует схему JSON. Различия в этой библиотеке в том, что она полностью полагается на TypeChecker (кроме enum) и сохраняет TypeAliases в качестве ссылок. По умолчанию он используется в качестве подключаемого модуля, поэтому он генерирует файлы * .schema.json во время компиляции tsc. Может также использоваться программно. Имея это в виду, я думаю, что разумно добавить небольшую заметку в файл readme об ограничении крайнего случая для enum. - person Ron S.; 11.12.2019
comment
Или, возможно, было бы лучше просто выдать предупреждение, если мы не можем получить значение. - person Ron S.; 11.12.2019