microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.87k stars 12.47k forks source link

export default: support inlined unique symbol #46139

Open mfulton26 opened 3 years ago

mfulton26 commented 3 years ago

Suggestion

🔍 Search Terms

✅ Viability Checklist

My suggestion meets these guidelines:

⭐ Suggestion

Make export default Symbol() emit type declarations for a unique symbol instead of simply a symbol.

📃 Motivating Example

export unique symbol as default without needing to declare it separately first

💻 Use Cases

I'm building a library that exports various symbols for use but I found that in order to use them as unique symbols that I have to declare them separately from exporting them. This isn't required for JavaScript at runtime and I'd like to be able to not need to name my symbol before exporting.

e.g.

export default Symbol();

emits

declare const _default: symbol;
export default _default;

and when used as a property name results in an error:

A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)

while

const symbol = Symbol();
export default symbol;

emits

declare const symbol: unique symbol;
export default symbol;

and works as expected as a property name

it's a small thing but if TypeScript could infer the type as the type of the default export of the module that'd be helpful and wouldn't cause writing the code differently to get correct typing

craigphicks commented 3 years ago

The issue "Incorrent error message (ts1166) when symbol is in fact a literal unique symbol given by Symbol.for('wellknownname')"

42601 is related. It is also about valid unique symbol giving the exact same error. Only those symbols are created using the little used Symbol.for('wellknownname')" interface, and not exported.

In fact, Symbol.for is not much use in Typescript unless the name strings are typed, but in that case might as well import symbols from a library, as is done by the OP. But I just thought I'd mention it, because ...

a-tarasyuk commented 3 years ago

UniqueSymbol can be created only for the following declarations https://github.com/microsoft/TypeScript/blob/7108646713da7bf82b04eea39e7ff89946e280d3/src/compiler/utilities.ts#L1494-L1498

if (isValidESSymbolDeclaration(node)) {
    const symbol = getSymbolOfNode(node);
    const links = getSymbolLinks(symbol);
    return links.uniqueESSymbolType || (links.uniqueESSymbolType = createUniqueESSymbolType(symbol));
}

It seems creating UniqueESSymbolType for the ExportAssignment node is the wrong way. @sandersn Maybe you have thoughts on how to properly fix this issue :smirk:

sandersn commented 3 years ago

@weswigham has spent way more time thinking about declaration emit (and unique symbol) than I have.

schlusslicht commented 1 year ago

I'm not sure if this is related or even intended behaviour, but:

When aliasing or proxying Symbol() calls, the returned value loses its type uniqueness and cannot be used as computed property name. Not sure how to word this properly so I'll just give You an example (view in playground):

const symbolAlias = Symbol;
const symbolProxy = new Proxy(Symbol, { });

const s1 = Symbol('1');
const s2 = symbolAlias('2');
const s3 = symbolProxy('3');

const obj = {
    [s1]: '1', // works
    [s2]: '2', // works
    [s3]: '3', // works
}

class Class {
    [s1] = '1'; // works
    [s2] = '2'; // error 1166
    [s3] = '3'; // error 1166
};