abstrakt syntakstræ - Kan TypeScript Compiler API opløse værdien af et udtryk?
Jeg spekulerer på, om der er en måde at bestemme den endelige værdi for et udtryk (ved hjælp af TypeScript-kompilatorens API), som ville fungere på en måde, der ligner getTypeAtLocation.
Tag følgende eksempel:
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]
}
- I dette eksempel vil C, D og E ikke fungere med getConstantValue().
- D er let at analysere, men C og E er eksempler på dybere problemer på grund af de anvendte udtryk, reference osv.
Mit håb er, at der findes en delvis løsning, som ikke vil kræve, at man skriver en kompleks rekursiv parser, der dækker alle mulige eventualiteter for et initializer-udtryk. Hvis intet er tilgængeligt i TS, kunne tredjeparts open-source kode også være nyttig.
Ellers vil enhver detalje, som kan hjælpe mig med at kende alle mulige nodevariationer på dette sted, være nyttig!
Jeg har allerede et robust rekursivt parser-system på plads, som jeg primært bruger til at gennemgå Type-strukturen, men det kan også implementeres for Node/Kind. Jeg vil mest af alt gerne være sikker på, at jeg ikke unødigt roder koden til eller genopfinder hjulet, før jeg går den vej.
Tak!
Rediger
Davids svar nedenfor er korrekt - der er ingen måde at gøre det på med TS.
Ved at udforske det nærmere, for at dække alle tilfælde, ville faktisk evaluering være påkrævet. Dette kunne blive rodet og være umuligt i mange tilfælde (se kommentarer nedenfor).
Heldigvis ser det ud til, at TypeScript generelt er i stand til at få svaret i tilfælde af enums med metoden getConstantValue().
Som en backup vil vi rekursivt parse initialisatornoden, der dækker nogle få udtryks tilfælde (som følger):
// 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
],
]);
Hvis nogen har nogen ideer til at udvide det lidt uden at gå helt amok, er I velkomne til at kommentere nedenfor!
Svar #1
Dette er nyttigt at gøre, men compileren har ikke noget indbygget til at løse udtryksværdier. Den bekymrer sig ikke om det... getConstantValue på typekontrollen er specifikt til enum-medlemmer.
Jeg tror heller ikke, at der er noget bibliotek, der kan hjælpe med dette i øjeblikket... i hvert fald har jeg ikke set et. Måske vil du eller en anden gerne lave et? Det ville være et sjovt projekt!
Under alle omstændigheder er dette bare mine overordnede tanker, som ville virke, men som kunne forbedres:
Finder ejendomstildelingsværdi
Det er lidt svært at gøre dette, fordi du skulle tjekke, om noget ændrede g.a før det bliver tildelt enum-medlemmet:
const g = { a: 'hello' };
g.a = 'other string';
Hvis du virkelig vil implementere noget lignende, så tror jeg, at en måde at gøre det på er at få symbolet for g.a (getSymbolAtLocation), og derefter kigge på ejendomstildelingen i #valueDeclaration ejendommen. Fra den ejendomstildeling bliver du nødt til at gå ned gennem noderne i træet for at få den sidste tildeling til den ejendom før enum-medlemsdeklarationen (en person, der leder efter problemer, kunne genassignere ejendommen i en tidligere enum-medlemsinitializer). Hvis tildelingsudtrykket er en konstant eller et udtryk, du kan løse, så kender du værdien. Hvis det ikke er tilfældet, bliver du nødt til at gå til den deklaration og gå gennem træet derfra. Grundlæggende bliver det ret kompliceret, og du vil ikke altid kunne bestemme værdien statisk.
Måske kan en begrænsning ved din analyzer være, at strengtyper ikke er tilladt, i hvilket tilfælde en const-assertion bør bruges?
const g = { a: 'hello' } as const;
I så fald er getTypeAtLocation for a i g.a strengen litteraltypen "hello".
Indlejrede Arrays
For disse indlejrede arrays [ ...[1,2,3] ][2] kunne du gå gennem træet i postorden, løse array-elementerne, flade arrayet ud på grund af spread-syntaksen, og derefter få det tredje element i arrayet. Hvis man ved, at det kun leder efter det tredje element, kunne man måske optimere det og kun løse nok til at få det andet element (det kunne spare meget tid, hvis arrayet er noget som [...[g.a, 2, 3]]).
