Skip to content

fix(38750): Adding keyword-named properties to functions generates invalid declarations #38874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1180,18 +1180,35 @@ namespace ts {
fakespace.parent = enclosingDeclaration as SourceFile | NamespaceDeclaration;
fakespace.locals = createSymbolTable(props);
fakespace.symbol = props[0].parent!;
const declarations = mapDefined(props, p => {
const declarations = flatMap(props, p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined; // TODO GH#33569: Handle element access expressions that created late bound names (rather than silently omitting them)
}
getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.valueDeclaration);
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, fakespace, declarationEmitNodeBuilderFlags, symbolTracker);
getSymbolAccessibilityDiagnostic = oldDiag;
const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined);
return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl]));
const propName = unescapeLeadingUnderscores(p.escapedName);
if (isIdentifier(p.valueDeclaration.name) &&
p.valueDeclaration.name.originalKeywordKind &&
isKeyword(p.valueDeclaration.name.originalKeywordKind)) {
const name = createUniqueName(propName);
return [
transformProperty(name, type),
createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createNamedExports([createExportSpecifier(name, propName)]))
];
}
return transformProperty(propName, type);

function transformProperty(name: string | Identifier, type: TypeNode | undefined) {
return createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([createVariableDeclaration(name, type, /*initializer*/ undefined)]));
}
});
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);

if (!hasEffectiveModifier(clean, ModifierFlags.Default)) {
return [clean, namespaceDecl];
}
Expand Down
327 changes: 327 additions & 0 deletions tests/baselines/reference/nullPropertyName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
//// [nullPropertyName.ts]
function foo() {}
// properties
foo.x = 1;
foo.y = 1;

// keywords
foo.break = 1;
foo.case = 1;
foo.catch = 1;
foo.class = 1;
foo.const = 1;
foo.continue = 1;
foo.debugger = 1;
foo.default = 1;
foo.delete = 1;
foo.do = 1;
foo.else = 1;
foo.enum = 1;
foo.export = 1;
foo.extends = 1;
foo.false = 1;
foo.finally = 1;
foo.for = 1;
foo.function = 1;
foo.if = 1;
foo.import = 1;
foo.in = 1;
foo.instanceof = 1;
foo.new = 1;
foo.null = 1;
foo.return = 1;
foo.super = 1;
foo.switch = 1;
foo.this = 1;
foo.throw = 1;
foo.true = 1;
foo.try = 1;
foo.typeof = 1;
foo.var = 1;
foo.void = 1;
foo.while = 1;
foo.with = 1;
foo.implements = 1;
foo.interface = 1;
foo.let = 1;
foo.package = 1;
foo.private = 1;
foo.protected = 1;
foo.public = 1;
foo.static = 1;
foo.yield = 1;
foo.abstract = 1;
foo.as = 1;
foo.asserts = 1;
foo.any = 1;
foo.async = 1;
foo.await = 1;
foo.boolean = 1;
foo.constructor = 1;
foo.declare = 1;
foo.get = 1;
foo.infer = 1;
foo.is = 1;
foo.keyof = 1;
foo.module = 1;
foo.namespace = 1;
foo.never = 1;
foo.readonly = 1;
foo.require = 1;
foo.number = 1;
foo.object = 1;
foo.set = 1;
foo.string = 1;
foo.symbol = 1;
foo.type = 1;
foo.undefined = 1;
foo.unique = 1;
foo.unknown = 1;
foo.from = 1;
foo.global = 1;
foo.bigint = 1;
foo.of = 1;


//// [nullPropertyName.js]
function foo() { }
// properties
foo.x = 1;
foo.y = 1;
// keywords
foo["break"] = 1;
foo["case"] = 1;
foo["catch"] = 1;
foo["class"] = 1;
foo["const"] = 1;
foo["continue"] = 1;
foo["debugger"] = 1;
foo["default"] = 1;
foo["delete"] = 1;
foo["do"] = 1;
foo["else"] = 1;
foo["enum"] = 1;
foo["export"] = 1;
foo["extends"] = 1;
foo["false"] = 1;
foo["finally"] = 1;
foo["for"] = 1;
foo["function"] = 1;
foo["if"] = 1;
foo["import"] = 1;
foo["in"] = 1;
foo["instanceof"] = 1;
foo["new"] = 1;
foo["null"] = 1;
foo["return"] = 1;
foo["super"] = 1;
foo["switch"] = 1;
foo["this"] = 1;
foo["throw"] = 1;
foo["true"] = 1;
foo["try"] = 1;
foo["typeof"] = 1;
foo["var"] = 1;
foo["void"] = 1;
foo["while"] = 1;
foo["with"] = 1;
foo.implements = 1;
foo.interface = 1;
foo.let = 1;
foo.package = 1;
foo.private = 1;
foo.protected = 1;
foo.public = 1;
foo.static = 1;
foo.yield = 1;
foo.abstract = 1;
foo.as = 1;
foo.asserts = 1;
foo.any = 1;
foo.async = 1;
foo.await = 1;
foo.boolean = 1;
foo.constructor = 1;
foo.declare = 1;
foo.get = 1;
foo.infer = 1;
foo.is = 1;
foo.keyof = 1;
foo.module = 1;
foo.namespace = 1;
foo.never = 1;
foo.readonly = 1;
foo.require = 1;
foo.number = 1;
foo.object = 1;
foo.set = 1;
foo.string = 1;
foo.symbol = 1;
foo.type = 1;
foo.undefined = 1;
foo.unique = 1;
foo.unknown = 1;
foo.from = 1;
foo.global = 1;
foo.bigint = 1;
foo.of = 1;


//// [nullPropertyName.d.ts]
declare function foo(): void;
declare namespace foo {
var x: number;
var y: number;
var break_1: number;
export { break_1 as break };
var case_1: number;
export { case_1 as case };
var catch_1: number;
export { catch_1 as catch };
var class_1: number;
export { class_1 as class };
var const_1: number;
export { const_1 as const };
var continue_1: number;
export { continue_1 as continue };
var debugger_1: number;
export { debugger_1 as debugger };
var default_1: number;
export { default_1 as default };
var delete_1: number;
export { delete_1 as delete };
var do_1: number;
export { do_1 as do };
var else_1: number;
export { else_1 as else };
var enum_1: number;
export { enum_1 as enum };
var export_1: number;
export { export_1 as export };
var extends_1: number;
export { extends_1 as extends };
var false_1: number;
export { false_1 as false };
var finally_1: number;
export { finally_1 as finally };
var for_1: number;
export { for_1 as for };
var function_1: number;
export { function_1 as function };
var if_1: number;
export { if_1 as if };
var import_1: number;
export { import_1 as import };
var in_1: number;
export { in_1 as in };
var instanceof_1: number;
export { instanceof_1 as instanceof };
var new_1: number;
export { new_1 as new };
var null_1: number;
export { null_1 as null };
var return_1: number;
export { return_1 as return };
var super_1: number;
export { super_1 as super };
var switch_1: number;
export { switch_1 as switch };
var this_1: number;
export { this_1 as this };
var throw_1: number;
export { throw_1 as throw };
var true_1: number;
export { true_1 as true };
var try_1: number;
export { try_1 as try };
var typeof_1: number;
export { typeof_1 as typeof };
var var_1: number;
export { var_1 as var };
var void_1: number;
export { void_1 as void };
var while_1: number;
export { while_1 as while };
var with_1: number;
export { with_1 as with };
var implements_1: number;
export { implements_1 as implements };
var interface_1: number;
export { interface_1 as interface };
var let_1: number;
export { let_1 as let };
var package_1: number;
export { package_1 as package };
var private_1: number;
export { private_1 as private };
var protected_1: number;
export { protected_1 as protected };
var public_1: number;
export { public_1 as public };
var static_1: number;
export { static_1 as static };
var yield_1: number;
export { yield_1 as yield };
var abstract_1: number;
export { abstract_1 as abstract };
var as_1: number;
export { as_1 as as };
var asserts_1: number;
export { asserts_1 as asserts };
var any_1: number;
export { any_1 as any };
var async_1: number;
export { async_1 as async };
var await_1: number;
export { await_1 as await };
var boolean_1: number;
export { boolean_1 as boolean };
var constructor_1: number;
export { constructor_1 as constructor };
var declare_1: number;
export { declare_1 as declare };
var get_1: number;
export { get_1 as get };
var infer_1: number;
export { infer_1 as infer };
var is_1: number;
export { is_1 as is };
var keyof_1: number;
export { keyof_1 as keyof };
var module_1: number;
export { module_1 as module };
var namespace_1: number;
export { namespace_1 as namespace };
var never_1: number;
export { never_1 as never };
var readonly_1: number;
export { readonly_1 as readonly };
var require_1: number;
export { require_1 as require };
var number_1: number;
export { number_1 as number };
var object_1: number;
export { object_1 as object };
var set_1: number;
export { set_1 as set };
var string_1: number;
export { string_1 as string };
var symbol_1: number;
export { symbol_1 as symbol };
var type_1: number;
export { type_1 as type };
var undefined_1: number;
export { undefined_1 as undefined };
var unique_1: number;
export { unique_1 as unique };
var unknown_1: number;
export { unknown_1 as unknown };
var from_1: number;
export { from_1 as from };
var global_1: number;
export { global_1 as global };
var bigint_1: number;
export { bigint_1 as bigint };
var of_1: number;
export { of_1 as of };
}
Loading