-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Extension methods as part of Language Service (no compile time or runtime) #35280
Comments
I also want to be clear - I don't know to what extent Language Services deliver autocomplete functionality. If not, I guess you could argue that "it's up to IDE developers". But this won't happen unless there's a general backing. So in that case, I'd urge Typescript to at least say something like "For Visual Studio we'll support this according to these principles". That would probably pave way for others to follow |
I feel like this would just lead to endless fights on DefinitelyTyped about whether or not it was appropriate to mark any given function as extension |
Thinking about it a bit more, this would just be extremely problematic to implement in a way that appeared to work consistently - TS would literally have to query every extension method to see if its parameter type was compatible with the operand. This isn't something that fits in the perf budget for completions. And something we've learned from auto-import is that "but it's nice when it does work" language service features aren't good enough; it works 100% of the time or people think it's broken and bad. You can always write a language service plugin if you want to add this to your own experience. |
Fair point, maybe for a feature like this to work well is that DefinitelyTyped never adds that feature - it's only per team to "define their own", but with the ability to say something like "treat lodash's
I guess this comes down to normal case vs worst case, and I understand you must cater for the worst case too. But, isn't this something that hits C# or others too?
Thanks, maybe more appropriate |
@staeke I really agree with you that we need extension methods. But we'd better not overload I would suggest we use a separate notation for extensions, for example, Unfortunately the bind operator is staled and never advanced to stage 1 in TC39 process (though babel plugin is available many years). But I think we still have chance to revive it. Actually I have been working on reforming the old proposal about two months, and very coincidental, I plan to rename it to Extensions Proposal 😜 and want to present it in TC39 meetings next year.
@RyanCavanaugh Not every , but only extension methods imported or created in current scope. Actually if we want provide autocomplete suggestions for pipeline operator, we will have even worse situation. If we really worry about performance, we always have a compromise solution: do not provide autocomplete for extensions and pipeline by default but allow users enable it if they want.
This is must-to-have! We need some easy way to use current libraries as extensions. For example: import * as lo from 'lodash'
const extension {first, last} = lo // just a example, we still need to investigate the syntax
let a = [1, 2, 3]
a::last() // 3 |
@RyanCavanaugh and @hax: Extension methods already exists! Eh...what? Well, I thought I'd write a language service plugin, only to realize, they actually work well enough for me already, with In terms of syntax, this works and is terse on the consumer side, but maybe a little verbose on the provider side (less of a problem). If, and that's a big if as it's not necessary, you wanted to fix that here are some thoughts: // First simplify extending types in general, by skipping the declare module syntax.
// Rather reuse "extends" (symbol after is non-ambiguous, auto-import works.)
// Also you can choose whether it's just typings (abstract, no implementation)
// or an expected prototype override (implementation) which will add to prototype
// On interfaces, this means Object.prototype
extends Date {
isLeapYear(this: Date) { .. }
}
// Now we want symbols, and a way to export those so we allow that inline
extends Date {
[export symbol isLeapYear]: function(this: Date) {...}
} This would be equivalent to export const isLeapYear = Symbol()
function isLeapYearImpl(this: Date) { .. }
declare global {
export interface Date {
[isLeapYear]: typeof isLeapYearImpl;
}
}
Date.prototype[isLeapYear] = isLeapYearImpl; |
I honestly don't care about the technical insights anymore. I just want to get extension methods work in some way in typescript. It's just a pain when you came from C# or Kotlin. getSomething(url)
.transformIt(it => it+1)
.mapToSometing(myParameter) is so much more easy to read than
Due to treeshaking, we saw many fluent APIs killed and replaced by dinstinct functions in the last years. And you can't see any suggestions of the IDE anymore, you just have to know what you can use. It really is a major drawback. |
Search Terms
extension methods, language service, compiler api
Suggestion
With the risk of you stopping to read here, this is related to extension methods: #9
I think @RyanCavanaugh made some great points about all the pitfalls of rewriting callsites, and modifying prototypes isn't great either. But here's my thinking. There's essentially 2 things extension methods bring:
I value 1. much higher. Which leads me to my suggestion. Why not just bring up an extension to the language service for autocomplete? And an appropriate transformation at code write time.
Use Cases
Examples
Consider e.g. the following code:
Then whenever somebody types
getSomeDate().
(notice the trailing.
), the autocomplete list would showDate
members, but also any found extension methods (maybe with a slightly altered visual representation). Say I toggled down and choseisLeapYear
, then the code would be transformed toisLeapYear(getSomeDate())
with the marker in the end. Or if multiple arguments are expected maybe after the first one.For supporting the optional chaining operator after typing
getSomeDate().?
, my suggestion would be transform to something likeapplyOpt(getSomeDate(), isLeapYear)
If you wanted to be fancy, you could rather transform it to a global
pipe
function (see tc39/proposal-pipeline-operator#146). Something like:The optional chaining would maybe be something like
Interfaces would then be naturally supported as well
Checklist
My suggestion meets these guidelines:
#9
The text was updated successfully, but these errors were encountered: