Skip to content
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

Support symbol properties on namespaces #36813

Open
5 tasks done
ExE-Boss opened this issue Feb 14, 2020 · 6 comments
Open
5 tasks done

Support symbol properties on namespaces #36813

ExE-Boss opened this issue Feb 14, 2020 · 6 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@ExE-Boss
Copy link
Contributor

ExE-Boss commented Feb 14, 2020

Search Terms

  • namespace symbol
  • namespace symbol property
  • namespace symbol properties

Suggestion

The ability to declare symbol properties on TypeScript namespaces.

Use Cases

See DefinitelyTyped/DefinitelyTyped#42154 (comment).

Examples

// doSomething.d.ts
import { promisify } from "util";

declare function doSomething(
	foo: any,
	onSuccessCallback: (result: string) => void,
	onErrorCallback: (reason: any) => void
);
declare namespace doSomething {
	function [promisify.custom](foo: any): Promise<string>;
}

export = doSomething;

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@DanielRosenwasser
Copy link
Member

Seems like this is mostly meant for the .d.ts representations - is that the intent? Because otherwise, you can already add computed properties to functions.

function blah() {

}

blah[Symbol.iterator] = 100;

One problem that @weswigham brought up with this is that assuming you're repurposing let/const/var to export these symbols, it would introduce a parsing ambiguity.

export namespace foo {
  export let [xyz] = 10; // currently parses as a binding pattern
}

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Feb 21, 2020
@ExE-Boss
Copy link
Contributor Author

ExE-Boss commented Dec 1, 2020

#40594 could probably help with this by allowing the following in namespaces:

declare namespace Reflect {
	const toStringTag: "Reflect";
	export { toStringTag as [Symbol.toStringTag] };
}

Seems like this is mostly meant for the .d.ts representations - is that the intent? Because otherwise, you can already add computed properties to functions.

Yes, this is intended for the .d.ts representations, which use namespaces for properties on functions:

// foo.ts
const kSymbol = Symbol('symbol');
function blah() {
	// ...
}

blah.foo = 'foo';
blah[kSymbol] = 100;
// generated foo.d.ts
declare const kSymbol: unique symbol;
declare function blah(): void;
declare namespace blah {
	var foo: string;
}

Playground link


// expected foo.d.ts
declare const kSymbol: unique symbol;
declare function blah(): void;
declare namespace blah {
	export var foo: string;
	var _kSymbol: number;
	export { _kSymbol as [kSymbol] };
}

@weswigham
Copy link
Member

weswigham commented Dec 1, 2020

I don't see how export { toStringTag as [Symbol.toStringTag] }; will ever be allowed in JS (I admit syntactically, the affordance makes sense), considering export bindings are hoisted, but the value of Symbol.toStringTag requires execution to occur to be known. We could allow it only in ambient contexts, but that has the possibility of stepping on whatever tc39 might do to allow symbol-named module members natively. The linked ecma262 thread in #40594 only allows literal strings in those positions.

@ExE-Boss
Copy link
Contributor Author

ExE-Boss commented Dec 1, 2020

I mean only allowing it in TypeScript’s namespace construct.

@weswigham
Copy link
Member

We could allow it only in ambient contexts, but that has the possibility of stepping on whatever tc39 might do to allow symbol-named module members natively.

😦

rwe added a commit to rwe/vscode-windows-process-tree that referenced this issue Nov 4, 2022
This defines properties on each of the exported asynchronous functions
which allow them to work with Node's `promisify`.

The existing callbacks were incompatible by default, because they:
  • are not given as the last arguments of those functions
  • do not accept optional "error" as their first arguments

In other words, they are not "Node-style".

This implementation chooses to reject promises rather than returning
`undefined` on those errors, as the callbacks do: presumably the
`undefined` value is used simply because the existing callback format
forgot to allow for an error parameter.

This defines both the modern `promisify.custom` symbol and the "old"
`__promisify__` property, since the former isn't actually describable in
TypeScript definitions yet, and the latter is still used in Node's
definition files (e.g. for `child_process` functions). See:
  • microsoft/TypeScript#36813DefinitelyTyped/DefinitelyTyped#42154
rwe added a commit to rwe/vscode-windows-process-tree that referenced this issue Nov 9, 2022
This defines properties on each of the exported asynchronous functions
which allow them to work with Node's `promisify`.

The existing callbacks were incompatible by default, because they:
  • are not given as the last arguments of those functions
  • do not accept optional "error" as their first arguments

In other words, they are not "Node-style".

This implementation chooses to reject promises rather than returning
`undefined` on those errors, as the callbacks do: presumably the
`undefined` value is used simply because the existing callback format
forgot to allow for an error parameter.

This defines both the modern `promisify.custom` symbol and the "old"
`__promisify__` property, since the former isn't actually describable in
TypeScript definitions yet, and the latter is still used in Node's
definition files (e.g. for `child_process` functions). See:
  • microsoft/TypeScript#36813DefinitelyTyped/DefinitelyTyped#42154
rwe added a commit to rwe/vscode-windows-process-tree that referenced this issue Nov 10, 2022
This defines properties on each of the exported asynchronous functions
which allow them to work with Node's `promisify`.

The existing callbacks were incompatible by default, because they:
  • are not given as the last arguments of those functions
  • do not accept optional "error" as their first arguments

In other words, they are not "Node-style".

This implementation chooses to reject promises rather than returning
`undefined` on those errors, as the callbacks do: presumably the
`undefined` value is used simply because the existing callback format
forgot to allow for an error parameter.

This defines both the modern `promisify.custom` symbol and the "old"
`__promisify__` property, since the former isn't actually describable in
TypeScript definitions yet, and the latter is still used in Node's
definition files (e.g. for `child_process` functions). See:
  • microsoft/TypeScript#36813DefinitelyTyped/DefinitelyTyped#42154
rwe added a commit to rwe/vscode-windows-process-tree that referenced this issue Nov 10, 2022
This defines properties on each of the exported asynchronous functions
which allow them to work with Node's `promisify`.

The existing callbacks were incompatible by default, because they:
  • are not given as the last arguments of those functions
  • do not accept optional "error" as their first arguments

In other words, they are not "Node-style".

This implementation chooses to reject promises rather than returning
`undefined` on those errors, as the callbacks do: presumably the
`undefined` value is used simply because the existing callback format
forgot to allow for an error parameter.

This defines both the modern `promisify.custom` symbol and the "old"
`__promisify__` property, since the former isn't actually describable in
TypeScript definitions yet, and the latter is still used in Node's
definition files (e.g. for `child_process` functions). See:
  • microsoft/TypeScript#36813DefinitelyTyped/DefinitelyTyped#42154
@webstrand
Copy link
Contributor

I believe this limitation prevents us from supplying types like

const exampleKey = Symbol();
globalThis[exampleKey] = 1;

since globalThis is a namespace, not a variable with a type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants