refactor(compiler-cli): update ast factories to account for type nodes

Updates the Babel and TypeScript AST factories to account to produce type nodes.
This commit is contained in:
Kristiyan Kostadinov
2026-03-11 14:48:06 +01:00
committed by Matthew Beck (Berry)
parent 1639fc565b
commit ed9750d6d7
4 changed files with 402 additions and 46 deletions
@@ -11,8 +11,10 @@ import {assert} from '../../../../linker';
import {
AstFactory,
BinaryOperator,
BuiltInType,
LeadingComment,
ObjectLiteralProperty,
Parameter,
SourceMapRange,
TemplateLiteral,
VariableDeclarationType,
@@ -104,40 +106,40 @@ export class BabelAstFactory implements AstFactory<
createFunctionDeclaration(
functionName: string,
parameters: string[],
parameters: Parameter<t.TSType>[],
body: t.Statement,
): t.Statement {
assert(body, t.isBlockStatement, 'a block');
return t.functionDeclaration(
t.identifier(functionName),
parameters.map((param) => t.identifier(param)),
parameters.map((param) => identifierWithType(param.name, param.type)),
body,
);
}
createArrowFunctionExpression(
parameters: string[],
parameters: Parameter<t.TSType>[],
body: t.Statement | t.Expression,
): t.Expression {
if (t.isStatement(body)) {
assert(body, t.isBlockStatement, 'a block');
}
return t.arrowFunctionExpression(
parameters.map((param) => t.identifier(param)),
parameters.map((param) => identifierWithType(param.name, param.type)),
body,
);
}
createFunctionExpression(
functionName: string | null,
parameters: string[],
parameters: Parameter<t.TSType>[],
body: t.Statement,
): t.Expression {
assert(body, t.isBlockStatement, 'a block');
const name = functionName !== null ? t.identifier(functionName) : null;
return t.functionExpression(
name,
parameters.map((param) => t.identifier(param)),
parameters.map((param) => identifierWithType(param.name, param.type)),
body,
);
}
@@ -228,10 +230,11 @@ export class BabelAstFactory implements AstFactory<
createVariableDeclaration(
variableName: string,
initializer: t.Expression | null,
type: VariableDeclarationType,
variableType: VariableDeclarationType,
type: t.TSType | null,
): t.Statement {
return t.variableDeclaration(type, [
t.variableDeclarator(t.identifier(variableName), initializer),
return t.variableDeclaration(variableType, [
t.variableDeclarator(identifierWithType(variableName, type), initializer),
]);
}
@@ -265,6 +268,79 @@ export class BabelAstFactory implements AstFactory<
return node;
}
createBuiltInType(type: BuiltInType): t.TSType {
switch (type) {
case 'any':
return t.tsAnyKeyword();
case 'boolean':
return t.tsBooleanKeyword();
case 'number':
return t.tsNumberKeyword();
case 'string':
return t.tsStringKeyword();
case 'function':
return t.tsTypeReference(t.identifier('Function'));
case 'never':
return t.tsNeverKeyword();
case 'unknown':
return t.tsUnknownKeyword();
}
}
createExpressionType(expression: t.Expression, typeParams: t.TSType[] | null): t.TSType {
const typeName = getEntityTypeFromExpression(expression);
return t.tsTypeReference(
typeName,
typeParams ? t.tsTypeParameterInstantiation(typeParams) : null,
);
}
createArrayType(elementType: t.TSType): t.TSType {
return t.tsArrayType(elementType);
}
createMapType(valueType: t.TSType): t.TSType {
const keySignature = identifierWithType('key', this.createBuiltInType('string'));
return t.tsTypeLiteral([t.tsIndexSignature([keySignature], t.tsTypeAnnotation(valueType))]);
}
transplantType(type: t.TSType): t.TSType {
if (t.isNode(type) && t.isTSType(type)) {
return type;
}
throw new Error('Attempting to transplant a type node from a non-Babel AST: ' + type);
}
}
function getEntityTypeFromExpression(expression: t.Expression): t.Identifier | t.TSQualifiedName {
if (t.isIdentifier(expression)) {
return expression;
}
if (t.isMemberExpression(expression)) {
const left = getEntityTypeFromExpression(expression.object);
if (!t.isIdentifier(expression.property)) {
throw new Error(
`Unsupported property access for type reference: ${expression.property.type}`,
);
}
return t.tsQualifiedName(left, expression.property);
}
throw new Error(`Unsupported expression for type reference: ${expression.type}`);
}
function identifierWithType(name: string, type: t.TSType | null): t.Identifier {
const node = t.identifier(name);
if (type !== null) {
node.typeAnnotation = t.tsTypeAnnotation(type);
}
return node;
}
function isLExpression(expr: t.Expression): expr is Extract<t.LVal, t.Expression> {
@@ -149,9 +149,16 @@ describe('BabelAstFactory', () => {
describe('createFunctionDeclaration()', () => {
it('should create a function declaration node with the given name, parameters and body statements', () => {
const stmts = statement.ast`{x = 10; y = 20;}`;
const fn = factory.createFunctionDeclaration('foo', ['arg1', 'arg2'], stmts);
const fn = factory.createFunctionDeclaration(
'foo',
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
stmts,
);
expect(generate(fn).code).toEqual(
['function foo(arg1, arg2) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
['function foo(arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
);
});
});
@@ -159,18 +166,32 @@ describe('BabelAstFactory', () => {
describe('createFunctionExpression()', () => {
it('should create a function expression node with the given name, parameters and body statements', () => {
const stmts = statement.ast`{x = 10; y = 20;}`;
const fn = factory.createFunctionExpression('foo', ['arg1', 'arg2'], stmts);
const fn = factory.createFunctionExpression(
'foo',
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
stmts,
);
expect(t.isStatement(fn)).toBe(false);
expect(generate(fn).code).toEqual(
['function foo(arg1, arg2) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
['function foo(arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
);
});
it('should create an anonymous function expression node if the name is null', () => {
const stmts = statement.ast`{x = 10; y = 20;}`;
const fn = factory.createFunctionExpression(null, ['arg1', 'arg2'], stmts);
const fn = factory.createFunctionExpression(
null,
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
stmts,
);
expect(generate(fn).code).toEqual(
['function (arg1, arg2) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
['function (arg1, arg2: number) {', ' x = 10;', ' y = 20;', '}'].join('\n'),
);
});
});
@@ -178,8 +199,14 @@ describe('BabelAstFactory', () => {
describe('createArrowFunctionExpression()', () => {
it('should create an arrow function with an implicit return if a single statement is provided', () => {
const expr = expression.ast`arg2 + arg1`;
const fn = factory.createArrowFunctionExpression(['arg1', 'arg2'], expr);
expect(generate(fn).code).toEqual('(arg1, arg2) => arg2 + arg1');
const fn = factory.createArrowFunctionExpression(
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
expr,
);
expect(generate(fn).code).toEqual('(arg1, arg2: number) => arg2 + arg1');
});
it('should create an arrow function with an implicit return object literal', () => {
@@ -190,9 +217,15 @@ describe('BabelAstFactory', () => {
it('should create an arrow function with a body when an array of statements is provided', () => {
const stmts = statement.ast`{x = 10; y = 20; return x + y;}`;
const fn = factory.createArrowFunctionExpression(['arg1', 'arg2'], stmts);
const fn = factory.createArrowFunctionExpression(
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
stmts,
);
expect(generate(fn).code).toEqual(
['(arg1, arg2) => {', ' x = 10;', ' y = 20;', ' return x + y;', '}'].join('\n'),
['(arg1, arg2: number) => {', ' x = 10;', ' y = 20;', ' return x + y;', '}'].join('\n'),
);
});
});
@@ -378,26 +411,37 @@ describe('BabelAstFactory', () => {
describe('createVariableDeclaration()', () => {
it('should create a variable declaration statement node for the given variable name and initializer', () => {
const initializer = expression.ast`42`;
const varDecl = factory.createVariableDeclaration('foo', initializer, 'let');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'let', null);
expect(generate(varDecl).code).toEqual('let foo = 42;');
});
it('should create a constant declaration statement node for the given variable name and initializer', () => {
const initializer = expression.ast`42`;
const varDecl = factory.createVariableDeclaration('foo', initializer, 'const');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'const', null);
expect(generate(varDecl).code).toEqual('const foo = 42;');
});
it('should create a downleveled variable declaration statement node for the given variable name and initializer', () => {
const initializer = expression.ast`42`;
const varDecl = factory.createVariableDeclaration('foo', initializer, 'var');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'var', null);
expect(generate(varDecl).code).toEqual('var foo = 42;');
});
it('should create an uninitialized variable declaration statement node for the given variable name and a null initializer', () => {
const varDecl = factory.createVariableDeclaration('foo', null, 'let');
const varDecl = factory.createVariableDeclaration('foo', null, 'let', null);
expect(generate(varDecl).code).toEqual('let foo;');
});
it('should create a variable declaration with a type', () => {
const initializer = expression.ast`42`;
const varDecl = factory.createVariableDeclaration(
'foo',
initializer,
'let',
factory.createBuiltInType('number'),
);
expect(generate(varDecl).code).toEqual('let foo: number = 42;');
});
});
describe('createRegularExpressionLiteral()', () => {
@@ -429,6 +473,55 @@ describe('BabelAstFactory', () => {
});
});
describe('createBuiltInType()', () => {
it('should create type annotations for built in types', () => {
expect(generate(factory.createBuiltInType('any')).code).toEqual('any');
expect(generate(factory.createBuiltInType('boolean')).code).toEqual('boolean');
expect(generate(factory.createBuiltInType('number')).code).toEqual('number');
expect(generate(factory.createBuiltInType('string')).code).toEqual('string');
expect(generate(factory.createBuiltInType('never')).code).toEqual('never');
expect(generate(factory.createBuiltInType('unknown')).code).toEqual('unknown');
expect(generate(factory.createBuiltInType('function')).code).toEqual('Function');
});
});
describe('createExpressionType()', () => {
it('should create a generic type annotation for an identifier', () => {
const id = factory.createIdentifier('MyType');
const type = factory.createExpressionType(id, null);
expect(generate(type).code).toEqual('MyType');
});
it('should create a generic type annotation for property access', () => {
const expr = factory.createPropertyAccess(factory.createIdentifier('ns'), 'MyType');
const type = factory.createExpressionType(expr, null);
expect(generate(type).code).toEqual('ns.MyType');
});
it('should create a generic type annotation with type parameters', () => {
const id = factory.createIdentifier('MyType');
const typeParam = factory.createBuiltInType('string');
const type = factory.createExpressionType(id, [typeParam]);
expect(generate(type).code).toEqual('MyType<string>');
});
});
describe('createArrayType()', () => {
it('should create an array type annotation', () => {
const elementType = factory.createBuiltInType('string');
const type = factory.createArrayType(elementType);
expect(generate(type).code).toEqual('string[]');
});
});
describe('createMapType()', () => {
it('should create an object type with an indexer', () => {
const valueType = factory.createBuiltInType('number');
const type = factory.createMapType(valueType);
expect(generate(type).code).toEqual('{\n [key: string]: number;\n}');
});
});
describe('setSourceMapRange()', () => {
it('should attach the `sourceMapRange` to the given `node`', () => {
const expr = expression.ast`42`;
@@ -10,8 +10,10 @@ import ts from 'typescript';
import {
AstFactory,
BinaryOperator,
BuiltInType,
LeadingComment,
ObjectLiteralProperty,
Parameter,
SourceMapRange,
TemplateLiteral,
UnaryOperator,
@@ -160,7 +162,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
createFunctionDeclaration(
functionName: string,
parameters: string[],
parameters: Parameter<ts.TypeNode>[],
body: ts.Statement,
): ts.Statement {
if (!ts.isBlock(body)) {
@@ -171,7 +173,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
undefined,
functionName,
undefined,
parameters.map((param) => ts.factory.createParameterDeclaration(undefined, undefined, param)),
parameters.map((param) => this.createParameter(param)),
undefined,
body,
);
@@ -179,7 +181,7 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
createFunctionExpression(
functionName: string | null,
parameters: string[],
parameters: Parameter<ts.TypeNode>[],
body: ts.Statement,
): ts.Expression {
if (!ts.isBlock(body)) {
@@ -190,14 +192,14 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
undefined,
functionName ?? undefined,
undefined,
parameters.map((param) => ts.factory.createParameterDeclaration(undefined, undefined, param)),
parameters.map((param) => this.createParameter(param)),
undefined,
body,
);
}
createArrowFunctionExpression(
parameters: string[],
parameters: Parameter<ts.TypeNode>[],
body: ts.Statement | ts.Expression,
): ts.Expression {
if (ts.isStatement(body) && !ts.isBlock(body)) {
@@ -207,13 +209,23 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
return ts.factory.createArrowFunction(
undefined,
undefined,
parameters.map((param) => ts.factory.createParameterDeclaration(undefined, undefined, param)),
parameters.map((param) => this.createParameter(param)),
undefined,
undefined,
body,
);
}
private createParameter(param: Parameter<ts.TypeNode>): ts.ParameterDeclaration {
return ts.factory.createParameterDeclaration(
undefined,
undefined,
param.name,
undefined,
param.type ?? undefined,
);
}
createIdentifier = ts.factory.createIdentifier;
createIfStatement(
@@ -330,7 +342,8 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
createVariableDeclaration(
variableName: string,
initializer: ts.Expression | null,
type: VariableDeclarationType,
variableType: VariableDeclarationType,
type: ts.TypeNode | null,
): ts.Statement {
return ts.factory.createVariableStatement(
undefined,
@@ -339,11 +352,11 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
ts.factory.createVariableDeclaration(
variableName,
undefined,
undefined,
type ?? undefined,
initializer ?? undefined,
),
],
this.VAR_TYPES[type],
this.VAR_TYPES[variableType],
),
);
}
@@ -372,6 +385,63 @@ export class TypeScriptAstFactory implements AstFactory<ts.Statement, ts.Express
});
return node;
}
createBuiltInType(type: BuiltInType): ts.TypeNode {
switch (type) {
case 'any':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
case 'boolean':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
case 'number':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
case 'string':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
case 'function':
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Function'));
case 'never':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
case 'unknown':
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
}
}
createExpressionType(expression: ts.Expression, typeParams: ts.TypeNode[] | null): ts.TypeNode {
const typeName = getEntityTypeFromExpression(expression);
return ts.factory.createTypeReferenceNode(typeName, typeParams ?? undefined);
}
createArrayType(elementType: ts.TypeNode): ts.TypeNode {
return ts.factory.createArrayTypeNode(elementType);
}
createMapType(valueType: ts.TypeNode): ts.TypeNode {
return ts.factory.createTypeLiteralNode([
ts.factory.createIndexSignature(
undefined,
[
ts.factory.createParameterDeclaration(
undefined,
undefined,
'key',
undefined,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
),
],
valueType,
),
]);
}
transplantType(type: ts.TypeNode): ts.TypeNode {
if (
typeof type.kind === 'number' &&
typeof type.getSourceFile === 'function' &&
ts.isTypeNode(type)
) {
return type;
}
throw new Error('Attempting to transplant a type node from a non-TypeScript AST: ' + type);
}
}
// HACK: Use this in place of `ts.createTemplateMiddle()`.
@@ -418,3 +488,17 @@ export function attachComments(
}
}
}
function getEntityTypeFromExpression(expression: ts.Expression): ts.EntityName {
if (ts.isIdentifier(expression)) {
return expression;
}
if (ts.isPropertyAccessExpression(expression)) {
const left = getEntityTypeFromExpression(expression.expression);
if (!ts.isIdentifier(expression.name)) {
throw new Error(`Unsupported property access for type reference: ${expression.name.text}`);
}
return ts.factory.createQualifiedName(left, expression.name);
}
throw new Error(`Unsupported expression for type reference: ${ts.SyntaxKind[expression.kind]}`);
}
@@ -178,8 +178,15 @@ describe('TypeScriptAstFactory', () => {
items: [body],
generate,
} = setupStatements('{x = 10; y = 20;}');
const fn = factory.createFunctionDeclaration('foo', ['arg1', 'arg2'], body);
expect(generate(fn)).toEqual('function foo(arg1, arg2) { x = 10; y = 20; }');
const fn = factory.createFunctionDeclaration(
'foo',
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
body,
);
expect(generate(fn)).toEqual('function foo(arg1, arg2: number) { x = 10; y = 20; }');
});
});
@@ -189,9 +196,16 @@ describe('TypeScriptAstFactory', () => {
items: [body],
generate,
} = setupStatements('{x = 10; y = 20;}');
const fn = factory.createFunctionExpression('foo', ['arg1', 'arg2'], body);
const fn = factory.createFunctionExpression(
'foo',
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
body,
);
expect(ts.isExpressionStatement(fn)).toBe(false);
expect(generate(fn)).toEqual('function foo(arg1, arg2) { x = 10; y = 20; }');
expect(generate(fn)).toEqual('function foo(arg1, arg2: number) { x = 10; y = 20; }');
});
it('should create an anonymous function expression node if the name is null', () => {
@@ -199,8 +213,15 @@ describe('TypeScriptAstFactory', () => {
items: [body],
generate,
} = setupStatements('{x = 10; y = 20;}');
const fn = factory.createFunctionExpression(null, ['arg1', 'arg2'], body);
expect(generate(fn)).toEqual('function (arg1, arg2) { x = 10; y = 20; }');
const fn = factory.createFunctionExpression(
null,
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
body,
);
expect(generate(fn)).toEqual('function (arg1, arg2: number) { x = 10; y = 20; }');
});
});
@@ -242,8 +263,14 @@ describe('TypeScriptAstFactory', () => {
items: [body],
generate,
} = setupExpressions('arg2 + arg1');
const fn = factory.createArrowFunctionExpression(['arg1', 'arg2'], body);
expect(generate(fn)).toEqual('(arg1, arg2) => arg2 + arg1');
const fn = factory.createArrowFunctionExpression(
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
body,
);
expect(generate(fn)).toEqual('(arg1, arg2: number) => arg2 + arg1');
});
it('should create an arrow function with an implicit return object literal', () => {
@@ -260,8 +287,14 @@ describe('TypeScriptAstFactory', () => {
items: [body],
generate,
} = setupStatements('{x = 10; y = 20; return x + y;}');
const fn = factory.createArrowFunctionExpression(['arg1', 'arg2'], body);
expect(generate(fn)).toEqual('(arg1, arg2) => { x = 10; y = 20; return x + y; }');
const fn = factory.createArrowFunctionExpression(
[
{name: 'arg1', type: null},
{name: 'arg2', type: factory.createBuiltInType('number')},
],
body,
);
expect(generate(fn)).toEqual('(arg1, arg2: number) => { x = 10; y = 20; return x + y; }');
});
});
@@ -447,7 +480,7 @@ describe('TypeScriptAstFactory', () => {
items: [initializer],
generate,
} = setupExpressions(`42`);
const varDecl = factory.createVariableDeclaration('foo', initializer, 'let');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'let', null);
expect(generate(varDecl)).toEqual('let foo = 42;');
});
@@ -456,7 +489,7 @@ describe('TypeScriptAstFactory', () => {
items: [initializer],
generate,
} = setupExpressions(`42`);
const varDecl = factory.createVariableDeclaration('foo', initializer, 'const');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'const', null);
expect(generate(varDecl)).toEqual('const foo = 42;');
});
@@ -465,15 +498,29 @@ describe('TypeScriptAstFactory', () => {
items: [initializer],
generate,
} = setupExpressions(`42`);
const varDecl = factory.createVariableDeclaration('foo', initializer, 'var');
const varDecl = factory.createVariableDeclaration('foo', initializer, 'var', null);
expect(generate(varDecl)).toEqual('var foo = 42;');
});
it('should create an uninitialized variable declaration statement node for the given variable name and a null initializer', () => {
const {generate} = setupStatements();
const varDecl = factory.createVariableDeclaration('foo', null, 'let');
const varDecl = factory.createVariableDeclaration('foo', null, 'let', null);
expect(generate(varDecl)).toEqual('let foo;');
});
it('should create a variable declaration with a type', () => {
const {
items: [initializer],
generate,
} = setupExpressions(`42`);
const varDecl = factory.createVariableDeclaration(
'foo',
initializer,
'let',
factory.createBuiltInType('number'),
);
expect(generate(varDecl)).toEqual('let foo: number = 42;');
});
});
describe('createRegularExpressionLiteral()', () => {
@@ -509,6 +556,62 @@ describe('TypeScriptAstFactory', () => {
});
});
describe('createBuiltInType()', () => {
it('should create keyword type nodes for simple types', () => {
const {generate} = setupStatements();
expect(generate(factory.createBuiltInType('any'))).toEqual('any');
expect(generate(factory.createBuiltInType('boolean'))).toEqual('boolean');
expect(generate(factory.createBuiltInType('number'))).toEqual('number');
expect(generate(factory.createBuiltInType('string'))).toEqual('string');
expect(generate(factory.createBuiltInType('never'))).toEqual('never');
expect(generate(factory.createBuiltInType('unknown'))).toEqual('unknown');
});
it('should create a type reference for "function"', () => {
const {generate} = setupStatements();
expect(generate(factory.createBuiltInType('function'))).toEqual('Function');
});
});
describe('createExpressionType()', () => {
it('should create a type reference for an identifier', () => {
const {items, generate} = setupExpressions('MyType');
const type = factory.createExpressionType(items[0], null);
expect(generate(type)).toEqual('MyType');
});
it('should create a type reference for a property access', () => {
const {items, generate} = setupExpressions('ns.MyType');
const type = factory.createExpressionType(items[0], null);
expect(generate(type)).toEqual('ns.MyType');
});
it('should create a type reference with type parameters', () => {
const {items, generate} = setupExpressions('MyType');
const typeParam = factory.createBuiltInType('string');
const type = factory.createExpressionType(items[0], [typeParam]);
expect(generate(type)).toEqual('MyType<string>');
});
});
describe('createArrayType()', () => {
it('should create an array type node', () => {
const {generate} = setupStatements();
const elementType = factory.createBuiltInType('string');
const type = factory.createArrayType(elementType);
expect(generate(type)).toEqual('string[]');
});
});
describe('createMapType()', () => {
it('should create a type literal with an index signature', () => {
const {generate} = setupStatements();
const valueType = factory.createBuiltInType('number');
const type = factory.createMapType(valueType);
expect(generate(type)).toEqual('{\n [key: string]: number;\n}');
});
});
describe('setSourceMapRange()', () => {
it('should attach the `sourceMapRange` to the given `node`', () => {
const {