import { Request } from 'express';
import { Controller, Get, Post } from './core';
@Controller('/test')
export class TestController {
@Get()
async getAll() {
return 'TEST !!!';
}
@Get('/a')
async getA() {
return 'WTF A';
}
@Post('/b')
async setB(req: Request) {
return `Request Body is ${JSON.stringify(req.body)}`;
}
}
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'bar',
templateUrl: './bar.component.html',
styleUrls: ['./bar.component.css']
})
export class BarComponent implements OnInit {
@Input() bar: string
constructor() { }
ngOnInit() {
}
}
import { Component, State } from 'react';
@Component()
export class CounterComponent extends Component {
@State() count: number = 1;
addOne = () => {
this.count += 1;
}
render() {
return (
<div>
count: {this.count}
<button onClick={this.addOne}>add one</button>
</div>
);
}
}
<template>
<div>
<button v-on:click="decrement">-</button>
{{ count }}
<button v-on:click="increment">+</button>
</div>
</template>
<script>
import 'reflect-metadata';
import { Vue, Component, Prop } from 'vue-property-decorator';
@Component
export default class Counter extends Vue {
@Prop() initialCount: number;
count = this.initialCount;
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
</script>
export interface CounterProps {
initialCount: number;
}
@Hookable()
class Counter extends HookableComponent<CounterProps> {
@State() count = this.props.initialCount;
addOne = () => {
this.count += 1;
}
render() {
return (
<div>
<p>Count: {this.count}</p>
<p>Initial count: {this.props.initialCount}</p>
<button onClick={this.addOne}>Increase</button>
</div>
);
}
}
import "reflect-metadata";
const HOOK_METADATA = "__hooks__";
export abstract class HookableComponent<P = {}> {
constructor(public props: P) {}
abstract render(): JSX.Element;
}
type HookableHook = (instance: HookableComponent) => void;
const defineHookMetadata = (target: Object, hook: HookableHook) => {
const hooks = Reflect.getMetadata(HOOK_METADATA, target) || [];
hooks.push(hook);
Reflect.defineMetadata(HOOK_METADATA, hooks, target);
};
export const State = (): PropertyDecorator => (target, propertyKey) => {
const hook: HookableHook = instance => {
const initialValue = instance[propertyKey as keyof typeof instance];
const [value, setvalue] = useState(initialValue);
Reflect.defineProperty(instance, propertyKey, {
get: () => value,
set: setvalue
});
};
defineHookMetadata(target, hook);
};
export const Hookable = (): ClassDecorator => target => {
function HookableComponent(props: any) {
const ref = useRef<HookableComponent>();
if (!ref.current) {
ref.current = Reflect.construct(target, [props]);
}
const instance = ref.current!;
instance.props = props;
const hooks: HookableHook[] = Reflect.getMetadata(HOOK_METADATA, instance);
hooks.forEach(hook => hook(instance));
return instance.render();
}
HookableComponent.name = target.name;
return HookableComponent as any;
};
You have to understand how this works in JavaScript, which is very different from how it works in most languages
Without unstable syntax proposals, the code is very verbose.
class SomeComponent extends React.Component {
constructor() {
this.onClick = this.onClick.bind(this);
}
onClick() {
// ...
}
}
class SomeComponent extends React.Component {
onClick= () => {
// ...
}
}
Still on stage 2 (Draft)
interface Theme {
// ...
}
const ThemeContext = createContext<Theme>(defaultTheme);
@Hookable()
class SomeComponent extends HookableComponent<CounterProps> {
@Context(ThemeContext) theme: Theme;
// ...
}
const SomeComponent: FC = () => {
const theme = useContext(ThemeContext);
};
@Hookable()
class CounterMixin extends HookableMixin<CounterProps> {
@State() count = this.props.initialCount;
addOne() {
this.count++;
}
}
@Hookable()
class TwoCounter extends Mixins(CounterMixin, CounterMixin) {
// ...
}
function createCouterMixin(config) {
@Hookable()
class CounterMixin extends HookableMixin<CounterProps> {
@State() count = this.props.initialCount;
addOne() {
this.count++;
}
}
return CounterMixin;
}
@Hookable()
class TwoCounter extends Mixins(CounterMixin(/* ... */), CounterMixin(/* ... */) {
// ...
}
function useCounter(initialCount: number) {
return useState(initialCount);
}
const TwoCounter: FC = props => {
const [count1, setCount1] = useState(props.initialCount1);
const [count2, setCount2] = useState(props.initialCount2);
};
So why Angular still use decorator and class ?
ahead of time compilation
(function () {
function fibonacci(x) {
return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
}
window.x = fibonacci(15);
})();
window.x = 610;
(function () {
var self = this;
['A', 'B', 42].forEach(function(x) {
var name = '_' + x.toString()[0].toLowerCase();
var y = parseInt(x);
self[name] = y ? y : x;
});
})();
_a = "A";
_b = "B";
_4 = 42;
In Ecosystem of ECMAScript
const [a,,b] = [1, 2, 3];
"use strict";
var _ref = [1, 2, 3],
a = _ref[0],
b = _ref[2];
const args = [1, 2, 3];
const foo = (...nums) => nums.reduce((acc, num) => acc + num, 0);
foo(...x);
"use strict";
var args = [1, 2, 3];
var foo = function foo() {
for (var _len = arguments.length, nums = new Array(_len), _key = 0; _key < _len; _key++) {
nums[_key] = arguments[_key];
}
return nums.reduce(function (acc, num) {
return acc + num;
}, 0);
};
foo.apply(void 0, x);
<script>
let count = 0;
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
/* App.svelte generated by Svelte v3.19.2 */
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
listen,
noop,
safe_not_equal,
set_data,
space,
text
} from "svelte/internal";
function create_fragment(ctx) {
let button;
let t0;
let t1;
let t2;
let t3_value = (/*count*/ ctx[0] === 1 ? "time" : "times") + "";
let t3;
let dispose;
return {
c() {
button = element("button");
t0 = text("Clicked ");
t1 = text(/*count*/ ctx[0]);
t2 = space();
t3 = text(t3_value);
},
m(target, anchor) {
insert(target, button, anchor);
append(button, t0);
append(button, t1);
append(button, t2);
append(button, t3);
dispose = listen(button, "click", /*handleClick*/ ctx[1]);
},
p(ctx, [dirty]) {
if (dirty & /*count*/ 1) set_data(t1, /*count*/ ctx[0]);
if (dirty & /*count*/ 1 && t3_value !== (t3_value = (/*count*/ ctx[0] === 1 ? "time" : "times") + "")) set_data(t3, t3_value);
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(button);
dispose();
}
};
}
function instance($$self, $$props, $$invalidate) {
let count = 0;
function handleClick() {
$$invalidate(0, count += 1);
}
return [count, handleClick];
}
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
export default App;
import { Component } from '@angular/core';
@Component({
selector: 'my-component',
template: '{{person.addresss.street}}'
})
class MyComponent {
person?: Person;
}
??
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.
import { Component } from '@angular/core';
@Component({
selector: 'my-component',
template: '{{person.addresss.street}}'
})
class MyComponent {
person?: Person;
}
my.component.ts.MyComponent.html(1,1): : Property 'addresss' does not exist on type 'Person'. Did you mean 'address'?
@Injectable()
class CounterService {
readonly count$ = new BehaviorSubject(0);
increment(num: number) {
this.count.next(this.count.value + number);
}
}
@Component({
selector: 'counter',
template: `
<button (click)="counter.increment(1)">add one</button>
<span>{{ counter.count$ | async }}</span>
`
})
class TwoCounterComponent {
count: number;
constructor(
public readonly counter: CounterSerivce
) {}
}
import React from 'react';
const ThemeContext = createContext();
const AuthContext = createContext();
const MyContext = createContext();
<ThemeContext.Provider>
<AuthContext.Provider>
<MyContext.Provider>
// ...