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

Vue TypeScript Friendly Class API #48

Closed
adityapurwa opened this issue Jun 22, 2019 · 13 comments
Closed

Vue TypeScript Friendly Class API #48

adityapurwa opened this issue Jun 22, 2019 · 13 comments

Comments

@adityapurwa
Copy link

adityapurwa commented Jun 22, 2019

This is not an RFC - this issue is written so we can discuss more on how to make Class API works perfectly with TypeScript. Discussion is welcome and highly appreciated!

Summary

This RFC tries to make Vue Class API TypeScript friendly by breaking some of the things that now exist in Vue but not really practical to use with TypeScript. This is TypeScript oriented, so expect a lot of things that are already in Vue to be broken so it could fit TypeScript designs.

Inspired by React TypeScript supports.

Basic example

The new Class API would be able to be used like:

interface State {
  counter: number;
}
interface Props {
  startValue: number;
}

class Counter extends Vue.Component<State, Props>{
  data(): State {
    return {
      counter: this.props.startValue
    }
  }
  render(){
    const props = this.props;
    const state = this.state;
    // Not actual JSX or template
    return (
      <button @click={this.addCounter}> Counter {{ state.counter }} </button>
    )
  }
  addCounter() {
    this.state.counter++;
  }
}

Motivation

The motivation behind this that we hope Vue will have first class TypeScript supports like React. It's been a breeze to use React with TypeScript, but couldn't say the same with Vue. Having first-class TypeScript support means we can reduce the number of bugs related to inconsistent typings and better coding assistance in IDE.

The Class API is well structured, so it is easier to read and explore, that is why we hoped that we can continue to improve the Class API.

Detailed design

The goal is to separate the main instance with user-land and type-land properties. We then won't have to worry about merging the user-land code and type-land code, we can safely annotate each part that we wanted to annotate.

So we might end breaking a lot of existing Vue code because everything would be removed from the main instance.

It might look like this:

abstract class Component<TState, TProps, TApis, TEvents> {
  props: TProps;
  abstract data(): TState;
  abstract render();
  abstract api: TApis;
  abstract events: TEvents;
}

TApi might be used to expose the API that the component exposes, such as its methods. And TEvents is used to expose events that can be fired or listened to by other components.

Drawbacks

  • It breaks a lot of things
  • It also breaks user code that doesn't even use TypeScript and won't benefit from it
  • The plugin and mixins system might need to be broken too to support TypeScript

Alternatives

The Function API is a good way of supporting TypeScript.

Adoption strategy

We can write tools that detect the user state and props, and then modify the code that accesses them so it got prefixed by this.state or this.props.

We can also make the state and props property live side by side with the user existing state and props that lives on the instance directly. But it might mean we have to support both designs on the run.

Unresolved questions

We still don't know how to utilize TypeScript and make these things better:

  • Mixins
  • Events
  • Plugins
@LinusBorg
Copy link
Member

Hi, thanks for wanting to contribute with an RFC.

However, you posted this as an issue whereas RFC are done as Pull requests.

So please follow these steps:

  1. fork this repository.
  2. in your fork, write out our proposal in a markdown file in the /active-rfcsdirectory, preferably in its own branch
  3. open a Pull request from your fork to this main repository.
  4. In the PRs comment body, give short Summary of your PR, then link to the rendered version of the md file that you added.
  5. Close this issue, as discussions should happen in the PR comments.

Please see existing RFCs for examples. If you have any more questions, feel free to ask here.

@LinusBorg
Copy link
Member

Also, your RFC should address points from the abandoned class API RFC that we already had, and how your approach intends to solve them or why they don't apply etc.

#17

@adityapurwa
Copy link
Author

Hi @LinusBorg and thank you for responding!

I am creating this as an issue because I wanted to have a discussion first about this with others, following:

Gathering feedback before submitting
It's often helpful to get feedback on your concept before diving into the level of API design detail required for an RFC. You may open an issue on this repo to start a high-level discussion, with the goal of eventually formulating an RFC pull request with the specific implementation design.

At the meantime, while gathering more feedback - I am currently trying to implement a PoC for this API.

If a discussion in a PR is preferred, I would close this issue and starts working on the PR.

Thank You!

@LinusBorg
Copy link
Member

Ah, I see. Well, that's totally fine. It's just that you already wrote the issue in the formulaic way of the actual RFC, so I thought you wanted to submit it as an RFC directly.

It's fine to discuss here, just make sure to give some context at the beginning of your post that this is not yet meant as an RFC. Otherwise people might be confused, as evidenced by me :-P

@adityapurwa
Copy link
Author

Oops, sorry for the confusion @LinusBorg - I edited the issue description, I hope it doesn't cause any more confusion. Sorry!

@smolinari
Copy link
Contributor

smolinari commented Jun 22, 2019

There are inherent issues with using Class based componets in general. It's the motivation for why React came up with hooks.

Please read....https://reactjs.org/docs/hooks-intro.html#classes-confuse-both-people-and-machines

However, we found that class components can encourage unintentional patterns that make these optimizations fall back to a slower path. Classes present issues for today’s tools, too. For example, classes don’t minify very well, and they make hot reloading flaky and unreliable. We want to present an API that makes it more likely for code to stay on the optimizable path.

I'm sure this is also what Evan knows and understands and is trying to avoid. 😃

Scott

@lovetingyuan
Copy link

Maybe TypeScript Transformer Plugin(typescript.CustomTransformers) is a possible way?

@adityapurwa
Copy link
Author

Hi @lovetingyuan, what would be the use case for the transformer plugin as I never used it?

@adityapurwa
Copy link
Author

I tried to create a sketch of the API design,

Demo available https://stackblitz.com/edit/vue-ts-class-api-0001

// A quick sketch of the API Design
// Not an actual Vue app
abstract class Component<TProps, TState>{  
  protected abstract data(): TState;    
  protected state: TState;
  abstract render();  
  
  constructor(protected props: TProps, protected onRefresh: (c: Component<TProps, TState>) => void) {
    this.constructProxyFromData();
    // This is to simulate Vue rendering logic
    this.onRefresh(this);
  }
  private constructProxyFromData(){
    const obj = this.data();
    this.state = new Proxy(obj as any, {
      set: (obj, prop, val) =>{
        obj[prop] = val;        
        this.onRefresh(this);        
        return true;
      }
    }) as any;    
  }
}

class Counter extends Component<{
  counter: number
}, {
  counter: number
}>{

  data(){
    return {
      counter: this.props.counter
    }
  }

  render(){
    // This would be the template in real Vue app
    return `<h1>Counter ${this.state.counter}</h1>`;
  }

  click(){
    // Simulates a button click
    this.state.counter++;
  }

}


const appDiv = document.getElementById("app");
// This would be written as <Counter counter="1" /> in template
const counter = new Counter({
  counter: 1
}, (c)=> { // simulates Vue rendering logic
  appDiv.innerHTML = c.render();
});

// Simulates click
setInterval(()=>counter.click(), 1000);

@xxyuze
Copy link

xxyuze commented Sep 6, 2019

Using mobx we can build a ui framework-independent data layer. You are free to choose to use vue or react.
And It has TypeScript Friendly Class API.

@ishowman
Copy link

Also, your RFC should address points from the abandoned class API RFC that we already had, and how your approach intends to solve them or why they don't apply etc.

#17

Now that class component is not support in Vue3, is there anyway to migrate vue2 class component to vue3 component? @LinusBorg

@LinusBorg
Copy link
Member

The vue-class-component is still being supported and has seen a release that's compatible with Vue 3.

so just continue using this.

the class API just won't be integrated into Vue core, which the dropped RFC was about.

@adityapurwa
Copy link
Author

adityapurwa commented Dec 7, 2023

I believe this is no longer relevant as we have better TypeScript support now. As the issue poster, I won't mind if this issue is closed. Thank you to everyone involved on this discussion.

cc @LinusBorg Please feel free to either close or keep this issue opens depending on your insight. Thank you!

@LinusBorg LinusBorg closed this as not planned Won't fix, can't repro, duplicate, stale Dec 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants