-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Using lookahead to detect generic JSX elements? #6395
Comments
We've gotten this request a number of times and it does seem reasonable in terms of parsing. |
Accepting PRs. This should be easy to parse but I think it's going to be some rather difficult work in the checker. |
@RyanCavanaugh whats status of this issue? import * as React from 'react';
interface MyTestProps<T> {
values: Array<T>;
selectHandler: (selectedVal: T) => void;
}
export class MyTest<T> extends React.Component<MyTestProps<T>, {}> {
render() {
return (
<ul>
{ this.props.values.map(val =>
<li>{val}</li>
) }
</ul>
);
}
}
class TestMyTest extends React.Component<{}, {}> {
render() {
return (
<MyTest values={[1, 2, 3, 4]} selectHandler={(val) => { } } />
);
}
} compiler throws error "type number[] is not assignable to type T[]" But in this example import * as React from 'react';
interface MyTestProps<T> {
values: Array<T>;
selectHandler: (selectedVal: T) => void;
}
export class MyTest<T> extends React.Component<MyTestProps<T>, {}> {
render() {
return (
<ul>
{ this.props.values.map(val =>
<li>{val}</li>
) }
</ul>
);
}
}
new MyTest({ values: ['a', 'b'], selectHandler: (v) => { v.length } }); selectHandler param v have type string; |
@RyanCavanaugh Could this be part of a future planned milestone? 2.1 or 2.2? Maybe a nicer API as workaround: we introduce a static function import React, { Component } from 'react';
interface FooProps<T> {
data: T;
someCallback: (data: T) => void;
}
class Foo<T> extends Component<FooProps<T>, void> {
// `of` is created here
static of<T>() {
return Foo as new () => Foo<T>;
}
render() {
// make something with this.props.data here...
return (
<div onClick={() => this.props.someCallback(this.props.data)}>
Click me!
</div>
);
}
}
interface CoolData {
baz: string;
}
const coolData: CoolData = {
baz: 'Hello'
};
// `of` is used here
const FooWithCoolData = Foo.of<CoolData>();
const Bar = () =>
<FooWithCoolData
data={coolData}
someCallback={data => console.log('I know data is CoolData here!')}
/>; Or if we don't want to use // new `of` inside `class Foo<T>`
static of<T>(t: T) {
return Foo as new () => Foo<T>;
}
// new usage of `of`
const FooWithCoolData = Foo.of(coolData); |
We'll be basically redoing JSX typechecking in 2.1 or 2.2 and can probably look at this at that time. |
Cool. Thank you for the update! :) |
Any update ? |
@RyanCavanaugh Please consider supporting at least type inference when possible. No need to add new syntax. Just having a generic component infer its type parameters solve a huge number use-cases. |
@alitaheri I believe the existing JSX work going on at #12107 will enable that scenario |
Still not working on [email protected]. The workaround works fine (
) but it's not only ugly and un-elegant, it is also really unclear for newcomers to react&typescript Either solution (allowing to specify the generic inside jsx or using type inference) would be really really welcome |
@MastroLindus in the meantime you might consider this workaround, which I find to be a bit simpler to read and write:
|
@zzzev I am aware of it( or others, like just a simple cast) but to define a new class just for the workaround somehow it feels even dirtier to me, even if in practice the two forms are not that much different... I simply cannot find one that I am really satisfied about |
We are still need a way to infer type parameter in the original example for generic React class component but here are some possible work around in some cases: (Note this is for 2.3 compiler) import * as React from "react";
type SelectProps<T > = { items: T[] }
function Select<T>(attr: SelectProps<T>) {...}
const someItems = [1, 2, 3, 4];
const someItems2 = ["ss"];
function renderJsx() {
return <Select items={someItems} />; // Ok
} Nightly & future 2.3.3 import * as React from "react";
type SelectProps<T > = { items: T[] }
class Select<T = any> extends React.Component<SelectProps<T>, any> { }
const someItems = [1, 2, 3, 4];
const someItems2 = ["ss"];
function renderJsx() {
return <Select items={someItems} />; // OK
} |
Would love to have this feature - would enable a lot of good scenarios :) |
Still as annoying as ever :D |
So it's more than a year now since the issue was opened, we are going for a Typescript 2.8 and is not even on the roadmap? I don't get it... |
maybe not so trivial to do |
@antanas-arvasevicius Apparently, the checker is the difficult part. If you read my initial bug, it was me addressing the other implementation aspect, mentioned in comments linked to from there. |
This should in theory become easier (possibly even trivial) once @weswigham 's work to refactor JSX typechecking is complete. I checked, and idle complaining that we haven't implemented popular a feature request has not caused new engineer time to appear. |
We actually do inference and generic instantiation for generic JSX components as of our last release, so we don't even actually really need anything more than the parse step for this anymore (I mean, ofc the call resolution need to be modified to check for type arguments before doing inference, but still - more done than before). |
Also, the OP is a tad out of date; Unless I'm mistaken, we don't even support parsing angle bracket style typecasts in JSX files, so the described ambiguity wouldn't exist. |
|
We require a constraint on the first type parameter. |
I stand corrected, idle complaining actually did improve the situation 😆 |
:D how about other issues related to TSX? Especially deprecating magic in
global JSX namespace and instead using type inference directly from
createElement? Allowing multiple JSX definitions would allow to use not
only react, but create many html like DSL's for e.g. database schema
definitions, permission roles, state machines which "config like
structures" could be directly translated to js.
…On Fri, 9 Mar 2018 at 01:05, Ryan Cavanaugh ***@***.***> wrote:
I stand corrected, idle complaining actually did improve the situation 😆
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6395 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AEAcnO_LOm60bVGqc6cTk5FYGTTvR85zks5tcbk2gaJpZM4HBBqf>
.
|
@antanas-arvasevicius We've merged code that allows us to look up the current global types in a namespace local to the factory function instead of globally already, which should be a significant improvement once libraries move to utilizing it. (And combined with the also recently introduced We want to move to just using the factory function's signature, but there's a slight problem - to create a perfectly well-typed factory function that we can actually do inferences for, you need |
Thank you for review of what's going on, it is more complicated/interesting
than it looked :) One year ago I had a trouble with JSX that I couldn't
declare that ElementType would be dependable on InstanceType e.g. <List>
would return real instance of object which is list and not an ADT like
structure as react vdom because not all frameworks works in vdom mode
extjs, isomorphic smartclient etc, but their GUI could be represented in
jsx way, my opened issue
#13890
Please consider it also, and if factory function is difficult/impossible to
implement maybe that proposal for ElementType could be released?
…On Fri, 9 Mar 2018 at 01:36, Wesley Wigham ***@***.***> wrote:
@antanas-arvasevicius <https://github.com/antanas-arvasevicius> We've
merged code that allows us to look up the current global types in a
namespace local to the factory function instead of globally
<#22207> already, which
should be a significant improvement once libraries move to utilizing it.
We *want* to move to just using the factory function's signature, but
there's a slight problem - to create a perfectly well-typed factory
function that we can actually do inferences for, you need ReturnType (and
InstanceType and FirstArgument) conditional type-types that are
overload-aware (otherwise you need compiler internal magic that assumes how
the factory function works or looks things up in a namespace, like we do
now). As is, we could basically do it, but we'd break overloading on
stateless function components (we'd always pick the last signature), which
would be no bueno; so fully moving away from the JSX namespace (anywhere)
might require that we either improve conditional types or implement real
"call" types first. We're actively looking at it, though!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6395 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AEAcnNcLWtsZ97TrJo3wowHEyXSiZzpRks5tccBngaJpZM4HBBqf>
.
|
This allows both the previous hack of: const StringFlatList = FlatList as { new(): FlatList<string> }; As well as should support upcoming generics in TSX: microsoft/TypeScript#6395 E.g. <FlatList<string> data=... renderItem=... />
Since this is not compatible with the JSX spec as it is right now, it would’ve been good to have the discussion over on the JSX spec repo. JSX already allows for nested elements in the attribute position |
Apologies - we've had this issue open for over two years with a pretty straightforward design desired from the beginning (implementation of typechecking being the real blocker, which we quietly implemented without the syntax in 2.7); I wasn't aware we had (or needed, since it's a type-based syntax addition, like type arguments for call or new expressions) any renewed outreach set up around this (probably something to do with me not having worked on it 3 years ago XD).
As an aside, occasionally I've heard people familiar with the jsx spec point that out (within the team, I think it's a favorite "I bet you didn't know x was valid jsx" fact). We still don't parse it - it's been years since we first noticed it and still haven't gotten a serious issue for it. Still seems underused. It is still broken in babel 6. (fixed in the 7 beta though, so it's got that going for it, though fragments are broken right now) Given how it was spoken of the last time it came up, its future isn't exactly golden (FYI, babylon7 is the only parser on ASTExplorer that can parse the fragment production, but babel can't actually transpile it right now. heh.). But it was brought up while I implemented this, IIRC 😉 We don't think there are any obvious conflicts (beyond the ones we already have to disambiguate with leading generic parameters and occasionally left shift token that occur elsewhere), either (unless someone's going to start suggesting silly things like jsx elements as property names in fragments or something). If you're looking for a brief on how it was implemented, in our case it's just, after the |
Initially from a couple different comments, but elaborated here.
Currently, in order to use a generic JSX element, you must alias both the type and interface to a non-generic specialization.
It would be a lot more convenient and consistent to use something like this:
In order to differentiate the latter, you can use a single token of lookahead in the parser to know if it's a generic JSX element:
<
, like in< Select <
, then consume the rest of the type as a generic type. After consuming that:>
, then finish the type as a generic type cast.{
(for spread attributes, when they get implemented), then parse the rest as a JSX element.>
, like in< Select >
, then finish the type as a simple type cast.{
, then parse the rest as a JSX element.I don't know of any reason that wouldn't be feasible to do, considering async arrow functions, which are already implemented, require potentially a whole call expression of parsing to differentiate between
let f = async (x, y)
, which is a call expression, andlet f = async (x, y) => x
, which is an async arrow function. (I feel sorry for whoever ends up implementing that in V8, which doesn't support any backtracking or much lookahead for memory reasons.)/cc @RyanCavanaugh
The text was updated successfully, but these errors were encountered: