Skip to content
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

Alternative syntax for type assertions to allow XML-like syntax extensions #296

Closed
thorn0 opened this issue Jul 29, 2014 · 122 comments
Closed
Assignees
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript

Comments

@thorn0
Copy link

thorn0 commented Jul 29, 2014

The current syntax for type assertions prevented the React team from adding TypeScript support to JSX. While E4X was a failure, XML-like syntax extensions is a good idea, which might make it into some next ECMAScript version. If it happens, it will be impossible to incorporate that syntax into TypeScript because of type assertions.

(ported from http://typescript.codeplex.com/workitem/2608)

@CyrusNajmabadi
Copy link
Contributor

"it will be impossible to incorporate that syntax into TypeScript because of type assertions."

This is not the case. As i mentioned in the original thread:

Hey Guys,

TS developer here (and guy who wrote the current 1.0 parser). I haven't read through the entire thread. However, from looking at a few examples of what you're asking for, this seems like it would be very easy to add in a fork of the typescript parser, if one were so willing.

The algorithm would be pretty darn simple. First, you'd prescan the file, looking for XML close tag candidates. i.e. you'd have a regexp that would match strings like (allowing for spaces between things). With that, you'd have a table mapping potential end tags to locations in the file.

You'd then need to augment the scanner and parser. The parser would be updated such that if it hit a potential opentag, it would need to speculatively parse ahead to see if this was actually XML or just a cast. The end-tag table would be helpful here as it would allow you to even decide if it was worthwhile to do that speculative parse. i.e. if you saw and had never seen in your doc, then there would be no point speculatively parsing. On the other hand, if you had and did have a tag later on, then it would be worth going forward.

If you never find the end tag, then you rewind parsing, and go ahead parsing the entity as a type assertion expression.

This approach is very similar to the one we already need to take today to deal with the ambiguity between type assertion expressions and generic arrow function expressions. Speculative lookahead is necessary (and already done here), so it's no stretch to augment it to support XML. The only little trick here is to get the end tags to ensure you don't speculatively parse every time, most of the time when it's not going to be fruitful.

@danquirk
Copy link
Member

Cyrus covered some of the issues here. Changing the syntax for type assertions would be a pretty serious breaking change. If we have to do so for some reason in the future (like a future EcmaScript version) then I think we cross that bridge when we get there.

@thorn0
Copy link
Author

thorn0 commented Aug 8, 2014

I didn't propose to change the syntax. I asked for adding an alternative one. It's not a breaking change.

@CyrusNajmabadi
Copy link
Contributor

@thorn0 I think the subtle issue here is that while you are proposing an alternative syntax for type-assertions, you seem to be implicitly stating that the existing type-assertion syntax would then be used for the xml-like syntax extensions. That part is what could be a significant breaking change.

That said, i still think this is very possible to do. However, i don't think it currently has enough interest to warrant the work right now. If you can show that ther'es enough interest in the community for this, that could change. Thanks!

@koistya
Copy link

koistya commented Jan 3, 2015

If you can show that ther'es enough interest in the community for this, that could change.
CyrusNajmabadi

There is about 100k monthly downloads of Rect.js library from the npm registry. Even if only 10% of this audience would willing to use TypeScript with React+JSX that looks big enough, IMHO. But, as of now, all this audience is steadily switching from TypeScript to Flow.

@DanielRosenwasser
Copy link
Member

For the record, you can easily subsume this functionality with template strings/tagged template strings, which will be available in 1.4.

@borekb
Copy link

borekb commented Mar 19, 2015

Now that TypeScript is a first-class citizen in Angular (2.0), it would be nice if React got some love too. There are ways to integrate them today but they are clunky, not very IDE-friendly, etc. I'd personally love if I could combine the two technologies seamlessly.

@Lenne231
Copy link

👍 definetly a must have! I would love to have type safety in my whole application, even in the views

@thorn0
Copy link
Author

thorn0 commented Mar 20, 2015

@CyrusNajmabadi

I think the subtle issue here is that while you are proposing an alternative syntax for type-assertions, you seem to be implicitly stating that the existing type-assertion syntax would then be used for the xml-like syntax extensions. That part is what could be a significant breaking change

It's not the case. Right, the existing syntax wouldn't be used in the files containing JSX expressions. However, the new syntax is all that is needed from TypeScript here. It's not a breaking change at all. The JSX expressions would be processed by the JSX processor, not by TypeScript. The TS compiler would get normal TypeScript code as its input, without any extensions. How is this a breaking change?

The React team would've been glad to add the support for the TS constructions to JSX, but the current syntax for type assumptions just makes it too complicated.

@borekb
Copy link

borekb commented Mar 20, 2015

This is a noob question but how comes that Flow doesn't have problem with angle brackets + generics + JSX and TypeScript does?

@thorn0
Copy link
Author

thorn0 commented Mar 20, 2015

There is no problems with generics, only with type assertions. Flow doesn't use angle brackets for them.

@borekb
Copy link

borekb commented Mar 20, 2015

Ah ok.

@Ciantic
Copy link

Ciantic commented Mar 30, 2015

Is the problem here with JSX where there is angle brackets? Couldn't JSX just simply have escaping possibility:

var a = 
    <SomeComponent>
    {
        if (someFunc\<generic\>(param)) {
            <NotGeneric>
        }
    }
    </SomeComponent>

Edit or in case of type-assertions:

var a = 
    <SomeComponent>
    {
        if ((\<SomeType\> a).test) {
            <NotGeneric>
        }
    }
    </SomeComponent>

Either way, I think JSX transformer should have escaping.

@DanielRosenwasser
Copy link
Member

It's definitely worth mentioning on this issue that @fdecampredon has a fork jsx-typescript

@Ciantic
Copy link

Ciantic commented Mar 30, 2015

Forking TypeScript 👎 is like expecting this one guy to use all his remaining free time to merge.

This thread does not intend to add JSX support to TypeScript but instead have possiblity to write TypeScript inside JSX syntax. And I think this problem should be taken to JSX transformer, and perhaps not discussed in TypeScript issue list at all.

@fdecampredon
Copy link

@Ciantic for what it worth I intend to maintain my jsx fork since I need it for my own projects.

@fdecampredon
Copy link

Also note that with the technique that @CyrusNajmabadi described I was able to support type assertion and jsx syntax, so I don't think there is a need for a new type assertion syntax.

@borekb
Copy link

borekb commented Mar 30, 2015

I will always have much harder time convincing my bosses to use a TypeScript clone rather than TypeScript itself. And with React Native, it's even more possible that React / JSX will be an important technology to support in the near future.

@fdecampredon
Copy link

@borekb while I understand your point what do you expect ?
I mean actually jsx syntax is a syntactic sugar created for React and completely dependent of React, for example:

<div />

Is compiled down to :

React.createElement('div', null);

It's not the purpose of typescript to support every dsl language created by every js framework out there, and I would be seriously surprised that the typescript team decide to support something like that directly.

If you don't want to use a fork you still have few options :

  • Use React object syntax :
var h = React.DOM;
h.div(null, h.span({className: 'something'}))// instead of  <div><span class="somehing" /></div>
  • Use React.createClass directly by renaming it
var h = React.createElement;
h('div', null, h('span', {className: 'something'},)); // instead of <div><span class="somehing" /></div>
  • Use string template, comment preprocessing method @jbrantly exposed etc ...

@borekb
Copy link

borekb commented Mar 30, 2015

@fdecampredon

It's not the purpose of typescript to support every dsl language created by every js framework out there

Agree, but I'd argue that React is not just "another JS framework" and I believe it (incl. JSX) should get some consideration from the TypeScript team. All your alternatives make it possible to combine TS + React which is good and I know about them but the convenience and/or toolability is simply not there. There is a reason why FB introduced JSX and there is a reason why it's not in string templates, comments etc.

@NoelAbrahams
Copy link

but I'd argue that React is not just "another JS framework"

It looks and smells rather like a JS framework. For it not to be a framework, it should be indispensable. I believe React deals with the specific problem of binding a JavaScript model to the DOM. The alternatives are Knockout JS and Angular JS.

@fdecampredon
Copy link

Agree, but I'd argue that React is not just "another JS framework" and I believe it (incl. JSX) should get some consideration from the TypeScript team. All your alternatives make it possible to combine TS + React which is good and I know about them but the convenience and/or toolability is simply not there. There is a reason why FB introduced JSX and there is a reason why it's not in string templates, comments etc.

That's why I made jsx-typescript :D

@borekb
Copy link

borekb commented Mar 30, 2015

Don't get me wrong, I'm glad your fork exists. However, for real-world projects, there are other important questions to be answered like does it have support in Visual Studio? In WebStorm / IntelliJ IDEA? How quickly is the fork updated for new releases of TypeScript? Etc. All these questions wouldn't exist if TS supported JSX out of the box which is why I think it would be valuable.

@fdecampredon
Copy link

Yup I understand @borek, in the meantimes I can answer your question:

  • I try to keep the compatibility with language service (and so visual studio) I didn't try vs myself (since I'm on OSX) but some people did and said it works perfectly (see Visual Studio integration fdecampredon/jsx-typescript#3 and API version mismatch fdecampredon/jsx-typescript#4)
  • WebStorm IntelliJ (last time I checked) don't use the language service layer (but their own) so I don't think it will ever be compatible with my fork
  • I plan to update the fork as soon as possible for each release of ts (in fact I'll start merging as soon as a 'release-xx' branch is created).

@fdecampredon
Copy link

Also note that jsx is not the only painful point of React/TS integration, see #1926 and #1947, jsx-typescript resolve that by creating special cases with type inference and jsx-element, I doubt the official ts compiler could make this kind of choice.

@borekb
Copy link

borekb commented Mar 30, 2015

Thanks for shedding some more light on it, @fdecampredon.

@mindplay-dk
Copy link

@duanyao

TS has no runtime type system, as long as you understand this, it is not hard to reason about the behavior of type assertion

TS has JS as runtime, so it definitely has a type system. What it doesn't have is type checking, maybe that's what you meant?

I agree though, that the variation in semantics is not that difficult to understand, if you understand the type system.

No matter which word or punctuation we choose, we should make sure it won't conflict with new keywords/operators added to ES in future. In this sense I prefer as, because it is already used for type casting in multiple languages, so ES will less likely use it.

Why is ES less likely to reuse familiar keywords/operators from other languages than we are?

I don't know the precise goals of the ES project, but they don't tend to deviate much from familiar syntax and keywords from other languages, when adding new features in recent iterations of the language. Reusing as was nearby and not too much of a stretch for us, why would it be for the ES people?

Come to think of it, if type-casting were added to ES in the future, reusing as would be less of a stretch for them, from the meaning of as in other languages like C#, since it would almost definitely involve some kind of run-time type conversion. We considered other options and most preferred as over proposed alternatives - without knowing the ES team, I'd say they would be unlikely not to use the as keyword for this feature.

Of course, nothing like that has been proposed for ES, I'm just playing devil's advocate here.

@duanyao
Copy link

duanyao commented May 18, 2015

@mindplay-dk TS has JS as runtime, but the type system of TS is not the type system of JS (obviously), the former only exists at compilation time. So type-related operators of TS are only applied at compilation time (except those already exists in JS).

I think ES/JS just can't implement a useful runtime casting, because of its prototype-based nature. There is no "interface" can exist at runtime, so you can't cast to an interface at runtime, this makes runtime casting half-baked. If all you want is just casting to a "class", you can already do it with instanceof, or even write your own casting function ( #3193 ). So a "runtime casting operator" is not quite useful in JS (even instanceof is arguably not quite useful). So casting operator should be out of the scope of ES/JS.

ActionScript 3 introduced as with runtime check, but as far as I know it also introduced a class-based type system and is even not backward compatible.

If ES would evolve its type system and runtime so radically like ActionScript 3 in future, I think TS itself would probably be unnecessary at that time.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this and removed In Discussion Not yet reached consensus labels May 18, 2015
@RyanCavanaugh RyanCavanaugh self-assigned this May 18, 2015
@RyanCavanaugh RyanCavanaugh added Committed The team has roadmapped this issue and removed Help Wanted You can do this labels May 18, 2015
@RyanCavanaugh
Copy link
Member

Comment from @AlicanC I am moving from #3203:

1- Is v the only reason for having a separate extension?

2- If as is compatible with both .ts and .tsx and v is only compatible with .ts, why would anyone want to use the less-compatible v? Why teach people v in books, if we are going to tell them it's incompatible with .tsx and they shouldn't get used to it a few pages later?

Assuming the answer for question 1 is yes-ish, why not:

Deprecate v in TS2,
Introduce something similar like <:T>v which will not be confused with JSX expressions and
Eliminate the need of having a separate extension?
var div = <:MyDiv><div />;

@RyanCavanaugh
Copy link
Member

Removing a commonly-used top-level production from a programming language essentially never happens, and for good reason. Syntactic breaking changes, especially ones that aren't absolutely necessary, are a good way to make sure no one uses your language.

We don't expect a large majority of people to be using TSX. Telling everyone else they have to change syntax for the sake of a feature they don't even use is rude at best.

@AlicanC
Copy link

AlicanC commented May 20, 2015

I don't think anyone would care since Microsoft customers are aIready used to spam that "Next" button to convert every VS solution they get their hands on.

You know that JavaScript suffers from past bad decisions like the "==" operator and the optional semicolons. An advantage of TS is that it is always compiled into something that every browser can run. With a proper "What's Changed?" blog post and a TS1-to-TS2 converter, a change like this would be no problem.

So I completely disagree that syntactic changes needs to be absolutely necessary. You could change the syntax every week and I wouldn't care as long as it was done for a good reason.

But, you believe that JSX will not be used by majority so not wanting to do a change like this is also completely understandable.

Let's hope I will be the only one who got upset to see JSX being treated like a second-class citizen.

@CyrusNajmabadi
Copy link
Contributor

@AlicanC

I don't think anyone would care since Microsoft customers are aIready used to spam that "Next" button to convert every VS solution they get their hands on.

TypeScript is used by people who don't use VS.

An advantage of TS is that it is always compiled into something that every browser can run. With a proper "What's Changed?" blog post and a TS1-to-TS2 converter, a change like this would be no problem.

This now means that people can't share code with other people who use different verisons of the compiler than them. It means people learning can't use code samples written for a previous version of the compiler. etc. etc.

Let's hope I will be the only one who got upset to see JSX being treated like a second-class citizen.

How is JSX being treated like a second class citizen?

@AlicanC
Copy link

AlicanC commented May 20, 2015

@CyrusNajmabadi

This now means that people can't share code with other people who use different verisons of the compiler than them. It means people learning can't use code samples written for a previous version of the compiler. etc. etc.

Why do you think that would be the case? We can even share code between Coffee and JavaScript thanks to conversion services. The choice between TS1 and TS2 isn't even like the choice between Coffee and JS. If you are using TS1, just run the converter and start using TS2.

And about learning material, they get outdated even when you don't introduce breaking changes to your language. Does my 10 year old JavaScript book has working examples in it? Yes. Should I care about Netscape support like it tells me to or take anything else serious in that book? No. It is actually worse because the examples work. Nothing warns you about using a bad practice, nothing tells you that you are doing something stupid. You can just keep learning from it and become the most useless developer in the universe.

And then you start telling people to use linters or you start projects like W3Fools to keep people away from bad practices. You can't break JavaScript or HTML because you will be breaking millions of websites. You can break TS or Coffee because the developer can just run a converter and keep rocking on.

(Again, I'm not saying these to support my proposal of changing the <T>v syntax. Though I could say these for the ES6-incompatible module/import syntax if you are going to decide on keeping it.)

How is JSX being treated like a second class citizen?

It doesn't look like you are eager to do this right. It looks like you want to implement it one way or another and just get over it. There is a project which introduces XML support to JSX. A lot more people could benefit from an implementation that not only supports React-like applications, but also XML.

The support could also be under .ts instead of .tsx with further discussion in #3022. For example, @RyanCavanaugh closed that issue by giving the {{ "hello world \}\} \{\{" }} example. I don't think he has no idea about Heredoc/Nowdoc or CDATA which are used to tackle problems like that. When you see people start pointing out false dead ends while they could easily further the discussion, you start to think that they just don't want to do it. If I read something like "How do we solve that problem then?" instead of "This is a dead end!", I would at least think that there were interest in what people think.

@thorn0
Copy link
Author

thorn0 commented May 20, 2015

@AlicanC Luckily, the React team tries to keep JSX abstracted away from React. The JSX spec even contains parts that aren't used by React (namely, XML-like namespaces). From what I can see, the TypeScript team adheres to the same principle and tries to come up with a generic support for abstract JSX, not for JSX+React only.

@CyrusNajmabadi
Copy link
Contributor

Why do you think that would be the case?

Because certain syntax constructs are no longer supported in both compilers. So a person cannot compile the same file in one compiler that they compile in the other.

No. It is actually worse because the examples work. Nothing warns you about using a bad practice, nothing tells you that you are doing something stupid. You can just keep learning from it and become the most useless developer in the universe.

None of the documentation about how to do type assertions is teaching you a bad practice.

It doesn't look like you are eager to do this right.

To me, Ryan laid out a very strong case for why the current approach is a good way to do things. For example providing a solution that is more compatible rather then less compatible can definitely be argued to be a better way to handle things depending on what you think is important.

It would probably also be good for the discussion to try to avoid overly loaded words like "right". We must all recognize that many people evaluate change with different weights to the pros/cons than us, and even altogether different criteria than us. There is rarely a case where something is truly 'right'. Rather, it's the best solution that's available given all the different things we need to balance. Please keep that in mind as we discuss this.

@AlicanC
Copy link

AlicanC commented May 20, 2015

@thorn0 The Careless documentation states:

Careless is a fork of Facebook's JSX, that allows custom tags, custom attributes and XML namespaces : the only (?) XML features missing from Careless are doctypes and XML comments.

If that statement is wrong, please correct us. If that is right, then TSX could be a JSX superset instead of a direct JSX implementation so XML dudes can benefit more from it.

@CyrusNajmabadi

Because certain syntax constructs are no longer supported in both compilers. So a person cannot compile the same file in one compiler that they compile in the other.

So are you going to have:

  • Two ways to do type assertions,
  • Two ways of importing files and
  • Three extensions (.tsx and .ts being 99% the same)

in your language to fight that? Isn't that a lot of pollution and possible confusion? I would rather break the language than have that much pollution.

None of the documentation about how to do type assertions is teaching you a bad practice.

As soon as you introduce the .tsx extension and v as T, people who want to do type assertion in .tsx will end up in this SO and get the wrong answer:
http://stackoverflow.com/questions/19461479/what-is-the-type-assertion-operator-for-in-typescript

So some of your learning material is already set to be wrong and suggesting (and using) <T>v will also be wrong since it won't work in .tsx files. <T>v will never be the answer for "How to do type assertion in TypeScript?" anymore because it will only be able to do type assertion in half of TypeScript.

Also, when support for ES6 Modules become stable, the answer for "How to import files in TypeScript?" will be "Use ES6 syntax." because teaching people a more universal solution will be better than teaching them a TS-specific solution.

Even without any breaking changes, you are already breaking your documentation and other learning material. People who are going to use <T>v type-asserted code sample in a .tsx file are already fucked.

"Oh crap, we don't have support for that." moments should not end up being resolved by adding another way to do the same thing. Either break <T>v (since learning material will go bad no matter what) or implement JSX in a way that is compatible with <T>v.

@danquirk
Copy link
Member

Isn't that a lot of pollution and possible confusion? I would rather break the language than have that much pollution.

Can you name some other successful languages that are designed like this? How many different ways does C# have to define anonymous functions due to its evolution over time? What's the correct way to use pointers in C++ now (*, smart, shared, etc)? There're a great many books, tutorials and SO answers that are now out of date. You are vastly underestimating the pain of breaking changes to most people, especially when weighed against convenience related changes that don't fix correctness issues or feature gaps. The history of programming languages bears this out pretty clearly (look at the schism caused by Python 3 and how it affected adoption).

@CyrusNajmabadi
Copy link
Contributor

So are you going to have:
Two ways to do type assertions

Yes. That gives people the compatibility they expect from a programming language, and enables us to sidestep a difficult ambiguity between JSX and TS. It's a win-win for me.

I would rather break the language than have that much pollution.

That's fine. But recognize that your priorities do not necessarily reflect everyone else's. There have been many times i've wanted to 'break' a language i'm using (including languages i've worked on). Depending on how good the change is, and how bad the break is, the breaks have been taken. But many many times, the break simply isn't worth it.

In this case, as with all others, an evaluation will be made as to whether or not a break is appropriate. However, my strong feeling is that this will not meet the bar. For me, the negative impact far outweighs the positives that have been listed. However, the team will consider it and feed in all our priorities to try to make the best decision possible in a scenario where there are no perfect answers.

@thorn0
Copy link
Author

thorn0 commented May 20, 2015

@AlicanC "a fork of Facebook's JSX" sounds like "a fork of HTML". It's not clear what they're trying to say there. Did they fork React's JSX transformer? Or did they 'fork' the JSX spec to change it somehow, which means they don't adhere to the original spec any more? In the latter case, they shouldn't expect much compatibility from other projects.

JSX is a "syntax extension to ECMAScript without any defined semantics". The JSX constructions are just a new building block for JS expressions. TypeScript needs to know their syntax to parse them, to type-check their elements (see below), and to output these syntax constructions as-is without transforming them into JS. The transformation should be done by some other transpiler - be it React's transformer, or Careless, or something else.

The tricky type-checking part is discussed in #3203. Unfortunately, the solution proposed there doesn't look React-agnostic at all.

@LeDominik
Copy link

Just my highly subjective 2¢: For me <T> for a type assertion just feels (and felt) wrong. Most type operations have operator-style with typeof for type guards and instanceof for the classic OO thing. Using as or a more specific astype seems more logical to me.

IMHO type assertion are one of the weirder constructs anyways because you still have to check with instanceof, which lots of people just forget. Autocomplete / IntelliSense will just give you the "correct" type once you decorated with <T> and most will stop thinking about it exactly there. It's just an assertion (erased in transpilation as per standard) without an assertprefix anywhere which is well known since C-macros to have no effect in production code. A really safe variation IMHO would be something like this:

if(let x = someFunc("circle") instanceof Circle) {
 ...
}

The <T> notation in my mind is generics, generics and of course XML, so in my mind it's like a opening gate for some kind of variation. And while a type assertion is of course connected to all this (types!) it's more an operation.

But as said: Just a user's opinion; probably the suggested construct collides with lot's of other cases :bowtie:. However (again as a user) the way React naturally uses HTML (which is just not going to go away as much as many dislike the XML-syntax) feels much more natural than any Angular 1.x felt or Angular 2 looks like... and Typescript is a language for the web that should not go down the road of complexity and confusion that most other languages went so early in life. I always found the "strict"; mode-switch inspiring, using it cleanly with versions would allow to upgrade step-by-step and assisted by tools. Old versions would just not support XML-constructs.

@thorn0
Copy link
Author

thorn0 commented Jun 29, 2015

As #3564 is merged into master, this issue can be closed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests