Skip to content

feat: Use declare class for namespaced types #961

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
67 changes: 16 additions & 51 deletions baselines/dom.generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19031,82 +19031,47 @@ declare namespace CSS {
}

declare namespace WebAssembly {
interface CompileError {
class CompileError {
}

var CompileError: {
prototype: CompileError;
new(): CompileError;
};

interface Global {
class Global {
constructor(descriptor: GlobalDescriptor, v?: any);
value: any;
valueOf(): any;
}

var Global: {
prototype: Global;
new(descriptor: GlobalDescriptor, v?: any): Global;
};

interface Instance {
class Instance {
constructor(module: Module, importObject?: Imports);
readonly exports: Exports;
}

var Instance: {
prototype: Instance;
new(module: Module, importObject?: Imports): Instance;
};

interface LinkError {
class LinkError {
}

var LinkError: {
prototype: LinkError;
new(): LinkError;
};

interface Memory {
class Memory {
constructor(descriptor: MemoryDescriptor);
readonly buffer: ArrayBuffer;
grow(delta: number): number;
}

var Memory: {
prototype: Memory;
new(descriptor: MemoryDescriptor): Memory;
};

interface Module {
class Module {
constructor(bytes: BufferSource);
static customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
static exports(moduleObject: Module): ModuleExportDescriptor[];
static imports(moduleObject: Module): ModuleImportDescriptor[];
}

var Module: {
prototype: Module;
new(bytes: BufferSource): Module;
customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
exports(moduleObject: Module): ModuleExportDescriptor[];
imports(moduleObject: Module): ModuleImportDescriptor[];
};

interface RuntimeError {
class RuntimeError {
}

var RuntimeError: {
prototype: RuntimeError;
new(): RuntimeError;
};

interface Table {
class Table {
constructor(descriptor: TableDescriptor);
readonly length: number;
get(index: number): Function | null;
grow(delta: number): number;
set(index: number, value: Function | null): void;
}

var Table: {
prototype: Table;
new(descriptor: TableDescriptor): Table;
};

interface GlobalDescriptor {
mutable?: boolean;
value: ValueType;
Expand Down
46 changes: 13 additions & 33 deletions baselines/webworker.generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5709,58 +5709,38 @@ interface Console {
declare var console: Console;

declare namespace WebAssembly {
interface Global {
class Global {
constructor(descriptor: GlobalDescriptor, v?: any);
value: any;
valueOf(): any;
}

var Global: {
prototype: Global;
new(descriptor: GlobalDescriptor, v?: any): Global;
};

interface Instance {
class Instance {
constructor(module: Module, importObject?: Imports);
readonly exports: Exports;
}

var Instance: {
prototype: Instance;
new(module: Module, importObject?: Imports): Instance;
};

interface Memory {
class Memory {
constructor(descriptor: MemoryDescriptor);
readonly buffer: ArrayBuffer;
grow(delta: number): number;
}

var Memory: {
prototype: Memory;
new(descriptor: MemoryDescriptor): Memory;
};

interface Module {
class Module {
constructor(bytes: BufferSource);
static customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
static exports(moduleObject: Module): ModuleExportDescriptor[];
static imports(moduleObject: Module): ModuleImportDescriptor[];
}

var Module: {
prototype: Module;
new(bytes: BufferSource): Module;
customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
exports(moduleObject: Module): ModuleExportDescriptor[];
imports(moduleObject: Module): ModuleImportDescriptor[];
};

interface Table {
class Table {
constructor(descriptor: TableDescriptor);
readonly length: number;
get(index: number): Function | null;
grow(delta: number): number;
set(index: number, value: Function | null): void;
}

var Table: {
prototype: Table;
new(descriptor: TableDescriptor): Table;
};

interface GlobalDescriptor {
mutable?: boolean;
value: ValueType;
Expand Down
2 changes: 1 addition & 1 deletion inputfiles/overridingTypes.json
Original file line number Diff line number Diff line change
Expand Up @@ -2538,7 +2538,7 @@
"Instance": {
"constructor": {
"override-signatures": [
"new(module: Module, importObject?: Imports): Instance"
"constructor(module: Module, importObject?: Imports)"
],
"force-references": [
{
Expand Down
112 changes: 90 additions & 22 deletions src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,16 +393,16 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
return `${i.name}<${typeParameters.map(t => t.name)}>`;
}

function emitConstant(c: Browser.Constant) {
function emitConstant(c: Browser.Constant, prefix = "") {
emitComments(c, printer.printLine);
printer.printLine(`readonly ${c.name}: ${convertDomTypeToTsType(c)};`);
printer.printLine(`${prefix}readonly ${c.name}: ${convertDomTypeToTsType(c)};`);
}

function emitConstants(i: Browser.Interface) {
function emitConstants(i: Browser.Interface, prefix = "") {
if (i.constants) {
mapToArray(i.constants.constant)
.sort(compareName)
.forEach(emitConstant);
.forEach(c => emitConstant(c, prefix));
}
}

Expand Down Expand Up @@ -635,12 +635,12 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
pType = convertDomTypeToTsType(p);
}
const required = p.required === undefined || p.required === 1;
const requiredModifier = required || prefix ? "" : "?";
const requiredModifier = required || (prefix && prefix !== "static ") ? "" : "?";
pType = p.nullable ? makeNullable(pType) : pType;
if (!required && prefix) {
pType += " | undefined"
}
const readOnlyModifier = p["read-only"] === 1 && prefix === "" ? "readonly " : "";
const readOnlyModifier = p["read-only"] === 1 && (prefix === "" || prefix === "static ") ? "readonly " : "";
printer.printLine(`${prefix}${readOnlyModifier}${p.name}${requiredModifier}: ${pType};`);
}

Expand Down Expand Up @@ -699,21 +699,25 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
}
}

function emitSignature(s: Browser.Signature, prefix: string | undefined, name: string | undefined, printLine: (s: string) => void) {
function emitSignature(s: Browser.Signature, prefix: string | undefined, name: string | undefined, printLine: (s: string) => void, isClass = false) {
const paramsString = s.param ? paramsToString(s.param) : "";
let returnType = convertDomTypeToTsReturnType(s);
returnType = s.nullable ? makeNullable(returnType) : returnType;
let returnSuffix = "";
if (!isClass || name !== "constructor") {
let returnType = convertDomTypeToTsReturnType(s);
returnType = s.nullable ? makeNullable(returnType) : returnType;
returnSuffix = `: ${returnType}`;
}
emitComments(s, printLine);
printLine(`${prefix || ""}${name || ""}(${paramsString}): ${returnType};`);
printLine(`${prefix || ""}${name || ""}(${paramsString})${returnSuffix};`);
}

function emitSignatures(method: { signature?: Browser.Signature[], "override-signatures"?: string[], "additional-signatures"?: string[] }, prefix: string, name: string, printLine: (s: string) => void) {
function emitSignatures(method: { signature?: Browser.Signature[], "override-signatures"?: string[], "additional-signatures"?: string[] }, prefix: string, name: string, printLine: (s: string) => void, isClass = false) {
if (method["override-signatures"]) {
method["override-signatures"]!.forEach(s => printLine(`${prefix}${s};`));
}
else if (method.signature) {
method["additional-signatures"]?.forEach(s => printLine(`${prefix}${s};`));
method.signature.forEach(sig => emitSignature(sig, prefix, name, printLine));
method.signature.forEach(sig => emitSignature(sig, prefix, name, printLine, isClass));
}
}

Expand Down Expand Up @@ -757,7 +761,9 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
function emitMembers(prefix: string, emitScope: EmitScope, i: Browser.Interface) {
const conflictedMembers = extendConflictsBaseTypes[i.name] ? extendConflictsBaseTypes[i.name].memberNames : new Set<string>();
emitProperties(prefix, emitScope, i);
const methodPrefix = prefix.startsWith("declare var") ? "declare function " : "";
const methodPrefix = prefix.startsWith("declare var") ? "declare function "
: prefix.startsWith("static") ? "static "
: "";
emitMethods(methodPrefix, emitScope, i, conflictedMembers);
if (emitScope === EmitScope.InstanceOnly) {
emitIteratorForEach(i);
Expand Down Expand Up @@ -810,15 +816,15 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
}
}

function emitConstructorSignature(i: Browser.Interface) {
function emitConstructorSignature(i: Browser.Interface, isClass = false) {
const constructor = typeof i.constructor === "object" ? i.constructor : undefined;

// Emit constructor signature
if (constructor) {
emitComments(constructor, printer.print);
emitSignatures(constructor, "", "new", printer.printLine);
emitSignatures(constructor, "", isClass ? "constructor" : "new", printer.printLine, isClass);
}
else {
else if (!isClass) {
printer.printLine(`new(): ${i.name};`);
}
}
Expand Down Expand Up @@ -868,11 +874,11 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
.forEach(emitNamedConstructor);
}

function emitInterfaceDeclaration(i: Browser.Interface) {
function processIName(iName: string) {
return extendConflictsBaseTypes[iName] ? `${iName}Base` : iName;
}
function processIName(iName: string) {
return extendConflictsBaseTypes[iName] ? `${iName}Base` : iName;
}

function emitInterfaceDeclaration(i: Browser.Interface) {
const processedIName = processIName(i.name);

if (processedIName !== i.name) {
Expand Down Expand Up @@ -997,6 +1003,63 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
}
}

function emitClassDeclaration(i: Browser.Interface, prefix = "") {
let finalExtendsName = i.extends && i.extends !== "Object" ? i.extends : null;
let extendsKeyword = "extends";
const finalImplements = (i.implements || []).sort().map(processIName);

if (finalExtendsName) {
if (finalExtendsName !== processIName(finalExtendsName) ||
!allInterfacesMap[finalExtendsName] ||
allInterfacesMap[finalExtendsName]["no-interface-object"]) {
finalImplements.unshift(processIName(finalExtendsName));
finalExtendsName = null;
}
}


if (i.comment) {
printer.printLine(`/** ${i.comment} */`);
}

if (finalImplements.length) {
printer.printLine(`interface ${getNameWithTypeParameter(i, i.name)} extends ${finalImplements.join(", ")} {}`);
}

printer.print(`${prefix}class ${getNameWithTypeParameter(i, i.name)}`);
if (finalExtendsName) {
printer.print(` ${extendsKeyword} ${finalExtendsName}`)
}
printer.print(" {");
printer.endLine();
}

function emitClass(i: Browser.Interface, prefix = "") {
printer.clearStack();
emitInterfaceEventMap(i);

emitClassDeclaration(i, prefix);
printer.increaseIndent();
emitConstructorSignature(i, true);

emitMembers(/*prefix*/ "", EmitScope.InstanceOnly, i);
emitConstants(i);
emitEventHandlers(/*prefix*/ "", i);
emitIndexers(EmitScope.InstanceOnly, i);

emitMembers(/*prefix*/ "static ", EmitScope.StaticOnly, i);
emitConstants(i, /*prefix*/ "static ");
if (iNameToConstParents[i.name] && iNameToConstParents[i.name].length) {
for (const parent of iNameToConstParents[i.name]) {
emitConstants(parent, /*prefix*/ "static ");
}
}

printer.decreaseIndent();
printer.printLine("}");
printer.printLine("");
}

function emitStaticInterface(i: Browser.Interface) {
// Some types are static types with non-static members. For example,
// NodeFilter is a static method itself, however it has an "acceptNode" method
Expand Down Expand Up @@ -1091,8 +1154,13 @@ export function emitWebIdl(webidl: Browser.WebIdl, flavor: Flavor, iterator: boo
namespace.nested.interfaces
.sort(compareName)
.forEach(i => {
emitInterface(i);
emitConstructor(i);
if (processIName(i.name) === i.name) {
emitClass(i);
}
else {
emitInterface(i);
emitConstructor(i);
}
});
namespace.nested.dictionaries
.sort(compareName)
Expand Down