-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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 passing generics to tagged template string #11947
Comments
From twitter, turns out this issue already exists: https://twitter.com/drosenwasser/status/797222514715824128. I think this is the correct syntax to use and the same thing I tried when I wanted to use generics with a template tag. |
If all you're doing is specifying the type argument for an output type (and the type system can't make any checks on that), that's not really a case for generics - that seems like a better case for a type assertion. The one thing that I do see as a benefit in the original use case is that you don't have to write |
@DanielRosenwasser The use-case doesn't bother me, it's only the feature I really care about 😄 I could very well want to restrict the types of parameters the tag can use to just values/keys of the type, for instance (E.g. current nightly features). Edit: I'd love to have the generic discussion somewhere, where's the best place to do that? Personally I like specify the return type in generics (where the return is valuable) for a couple of reasons:
|
I just ran into this trying to put together a small template library: It would be really nice if the first example worked (and would be the least astonishing, IMHO) |
Since it's awaiting more feedback, here's my feedback. I am pretty sure the use case is not only for return type. Consider signature: interface SimplifiedStyledFunction<P> {
<TNewProps>(strings: TemplateStringsArray, ...interpolations: ((props: P & TNewProps) => string)[]): StyledComponent<P & TNewProps>;
}
|
To give some technical background on this, there are four operations that we consider flavors of function invocations in the compiler:
The latter two do not allow explicit type arguments. My feeling is that both should be allowed, but given the demand, work to support tagged templates is reasonable and should have some more priority. |
If anyone does decide to send a PR, give us all a heads up here first. |
So I remembered the real reason we didn't do this originally. Right now, tagged templates are considered MemberExpressions. Those bind just a bit more tightly than a CallExpression or NewExpression. Since tagged templates will always bind more tightly, we'll always try to parse tagged template type arguments, even when we're going to parse out a call expression. If you take the following: foo<Bar>() A naive approach would do the following:
This approach means that for every call expression with generics, we have to perform steps 1.ii, through 1.iv even though we're going to throw away the result. While it's rare that you have to supply type arguments, it's less-than-ideal to do a re-parse every time. |
So, can a tagged template be treated as a variation of a CallExpression then? |
It already sort of is from the perspective of type-checking - during overload resolution and whatnot, we create a "virtual" argument for the template strings object. But it's important to keep the two syntactically separate. The solution is probably to make an intermediate parsing production that sits between MemberExpression and CallExpression. This is more a technical issue more than anything else. |
Some progress here? |
This makes perfect sense and as you already said, not ideal. How much impact this will really have on parser performance? |
@malimichael I really like your solution but what kind of other problems do are you referring to? |
@Havret The other problems happening now are more just inconveniences than anything else. Things like syntax highlighting and auto-completion no longer working for components wrapped by |
@beshanoe I don't think it's something that is confined to the TypeScript team illuminati 😄. The community can help here, but I definitely had some difficulties when I tried fixing it a while back. It's totally possible I'm missing something if you want to take a stab at it. I might give it another shot some time soon. |
The problem actually seems easier now that I look at it again. I'm going to take a second swing at it. |
Check out the fix at #23430 and let me know what you think! |
Thank you so very much @DanielRosenwasser and everyone else who assisted. I know myself and many others are extremely excited to use this when it is released. I am sure the styled-components community in particular will be very grateful. @styled-components/typers |
Yes, Thanks, Daniel! So how long does it take for something like this to go live? |
You're welcome! You can use it with the most recent nightly builds! To try it out quickly: npm install -g typescript@next or to try it out locally yarn add typescript@next
# or...
npm install typescript@next |
@DanielRosenwasser looks like there's a little bug with an implementation. interface IFooFn {
(strings: TemplateStringsArray): Promise<{}>;
<T>(strings: TemplateStringsArray): Promise<T>;
}
declare const fooFn: IFooFn;
export const promise = fooFn<number>``; // promise is of type Promise<{}> Such a code is now used in styled-components typings(styled-components/styled-components#1697 (comment)), and it should work out-of-the-box but it doesn't, because the overloaded fn without generic always hits first. |
@beshanoe Curious as to why the overload is needed. Wouldn't a default parameter for the generic solve this? type IFooFn<T = any> = (strings: TemplateStringsArray): Promise<T>; |
@marvinhagemeister I'm also interested why does styled-components typings have this line at all, but at least it revealed this buggy behavior |
@DanielRosenwasser could you please comment on #11947 (comment) |
Thanks for catching that, and sorry we missed this, but please don't comment on closed issues. It's almost impossible for us to read everything. |
@beshanoe As a workaround, you can use a generic default: interface IFooFn {
<T = {}>(strings: TemplateStringsArray): Promise<T>;
}
declare const fooFn: IFooFn;
export const promise = fooFn<number>``; // promise is of type Promise<number> |
I was trying to write some type definition for the styled-components project, but it looks like there is no way to specify a generic when using a tagged template. So... here is a subset of the typedef I'm writing:
This is how I'd like to use it:
I know that it can also be possible to override Input.propTypes manually[1], but I was wondering if this could be an useful addition to Typescript.
TypeScript Version: 2.0.3
[1] Or even write a custom component and just use the
styled(Component)
style`` syntaxThe text was updated successfully, but these errors were encountered: