- Notifications
You must be signed in to change notification settings - Fork13k
Support interpreting non-literal computed properties in classes as implicit index signatures#59860
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Support interpreting non-literal computed properties in classes as implicit index signatures#59860
Uh oh!
There was an error while loading.Please reload this page.
Conversation
@typescript-bot pack this |
typescript-bot commentedSep 4, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
typescript-bot commentedSep 4, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey@weswigham, I've packed this intoan installable tgz. You can install it for testing by referencing it in your
and then running There is also a playgroundfor this build and annpm module you can use via |
@typescript-bot test it |
typescript-bot commentedSep 6, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey@weswigham, the results of running the DT tests are ready. Everything looks the same! |
@weswigham Here are the results of running the user tests with tsc comparing Everything looks good! |
@weswigham Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: |
Seems like the angular codebase is gaining new errors? |
@weswigham Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
…t literal expressions
@typescript-bot test it Now I'm usingexactly the same index signature creation logic we use for object literal expressions, with all of it's flaws (like making a lot of |
typescript-bot commentedSep 9, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey@weswigham, the results of running the DT tests are ready. Everything looks the same! |
@weswigham Here are the results of running the user tests with tsc comparing Everything looks good! |
@weswigham Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: |
Hm. Still some new errors in angular, but everything else looks fine now. Guess I'll have to look at what those are. |
@weswigham Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
…gnature types - cant forget those
Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document themon our wiki's API Breaking Changes page. Also, please make sure@DanielRosenwasser and@RyanCavanaugh are aware of the changes, just as a heads up. |
@typescript-bot test it |
typescript-bot commentedSep 10, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey@weswigham, the results of running the DT tests are ready. Everything looks the same! |
@weswigham Here are the results of running the user tests with tsc comparing Everything looks good! |
@weswigham Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: |
There we go, no more new errors in angular - now just to see if top400 comes back cleaner, too. (I think it should, the most recent fix is squarely aimed at those errors, since they're the same cause as the ones in angular - forgetting to include the explicit prop types in the implied index signature type) |
@weswigham Here are the results of running the top 400 repos with tsc comparing Everything looks good! |
if (hasComputedStringProperty && !findIndexInfo(indexInfos, stringType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedStringProperty, 0, allPropertySymbols, stringType)); | ||
if (hasComputedNumberProperty && !findIndexInfo(indexInfos, numberType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedNumberProperty, 0, allPropertySymbols, numberType)); | ||
if (hasComputedSymbolProperty && !findIndexInfo(indexInfos, esSymbolType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedSymbolProperty, 0, allPropertySymbols, esSymbolType)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Are all of thefindIndexInfo
calls likely to cost a bunch or isindexInfos
just always short?
weswighamSep 18, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
In practice, it's always short (there's usually juststring
ornumber
indexes, if any) - in theory, a user could explicitly define hundreds of pattern literal index infos and it'd be bad, however, and you'd prefer you did the up-front work to make a map keyed on key type (if you had multiple kinds of computed names). But that's not code anyone's realistically writing.
fa0080f
intomicrosoft:mainUh oh!
There was an error while loading.Please reload this page.
pfumagalli commentedNov 24, 2024
Hmm... I hit a bit of a snag here. I have a class like this: importutilfrom'node:util'constinspect=util.inspect.customexportclassMyMapextendsMap<MyObjectA,MyObjectB>{[inspect](depth:number,options:util.InspectOptions):string{// my fancy code for making this look pretty...}// ... and here goes all the extra stuff I want} Now this compiles out as a exportdeclareclassMyMapextendsMap<MyObjectA,MyObjectB>{[x:symbol]:((depth:number,options:util.InspectOptions)=>string)|(()=>MapIterator<[string,string]>);// ... all the other niceties} ... so far so good (maybe???) but when I try to import my map in some other project I get an error:
Why? Because `` declares interfaceMap<K,V>{readonly[Symbol.toStringTag]:string;} Maybe the typescript compiler should merge all symbol declarations and write something like: exportdeclareclassMyMapextendsMap<MyObjectA,MyObjectB>{[x:symbol]:string|((depth:number,options:util.InspectOptions)=>string)|(()=>MapIterator<[string,string]>);// ... all the other niceties} I don't know what'd be theright solution in these cases. For my code, I can simply remove that assignment importutilfrom'node:util'exportclassMyMapextendsMap<MyObjectA,MyObjectB>{[util.inspect.custom](depth:number,options:util.InspectOptions):string{// my fancy code for making this look pretty...}// ... and here goes all the extra stuff I want} produces a more correct importutilfrom'node:util';exportdeclareclassMyMapextendsMap<MyObjectA,MyObjectB>{[util.inspect.custom]:((depth:number,options:util.InspectOptions)=>string)|(()=>MapIterator<[string,string]>);// ... all the other niceties} But I noticed that anytime I import a |
farfromrefug commentedOct 5, 2025
@weswigham i have an issue with that change. classProperty{readonlygetDefault:symbol;constructor(propertyName:string){constgetDefault=Symbol(propertyName+':getDefault');this.getDefault=getDefault;}} And in another class i do this: constmyProp=newProperty()classA{[myProp.getDefault]():boolean{returntrue;}} Before TS 5.7 it was working correctly. Now because of this PR it reports an error:
And i really dont see how to fix it. Can you help? |
Instead of silently dropping them (if they're declared by methods) as we do today.
This still has some minor issues and open questions, but is far enough along to share and talk about. Today, when you have a class like
that's just fine - no errors anywhere. But if you add a
you get an error that
A
has no symbol index signature. What's up with that? Well, we dropped the[a]
member entirely, silently. There are historical reasons for this, but they don't really hold much weight nowadays beyond "for back compat". Under non-strict, this can actually be insidious, as when you use an expression, the(new A())[a]
expression can silently beany
, since under non-strict mode, non-existing element accesses resolve toany
without error. With this PR,A[typeof a]
instead resolves to() => number
(the method's type) as we've created an implicit index signature for the computed name, just like we do for object literals.Technically, this involves late-binding the
__index
member of the class like we do other computed literal names (so you could call these "late bound index signatures" as I do in the code), but that's not so important compared to the effect. This is pretty different from how these kinds of indexes are built for object literals, but that's because object literals are built somewhat on-demand from their constituent declarations, rather than having a big table of declared metadata built up-front like classes and interfaces do.The big questions I'm still considering are:
A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.(1166)
be removed, too? Methods don't emit an error, which is what motivated me here, but all this logic equally applies to properties, so the error doesn't make too much sense for them now.noImplicitAny
or another (strict?) flag? (preferably not - this being flagged somewhat defeats the point, imo)And, ofc, this needs more tests, covering other key types (unions, pattern literals) and modifiers (static, readonly) on the computed names (though I've informally handled them in the code, I think) before this can actually be merged (though these probably have some coverage already from documenting our current behavior - they could stand to be more comprehensive).
This is, IMO, a worthwhile step towards unifying all of our computed property name behaviors. In an ideal world, a computed property name in a class, object, in a source or declaration file, could all be interpreted the same way (and thus, it could always be safe to copy one from one place or file kind into the other). Unfortunately, today we have some behavioral carve-outs from the time before we even meaningfully supported literal types, or even had
--strict
, that get in the way of that, like this.