Files
bun.sh/test/bun.js/decorators.test.ts
Colin McDonnell f7f1b60444 Add bun-types, add typechecking, add child_process types (#1475)
* Add bun-types to packages

* Improve typing

* Fix types in tests

* Fix dts tests

* Run formatter

* Fix all type errors

* Add strict mode, fix type errors

* Add ffi changes

* Move workflows to root

* Add workflows

* Remove labeler

* Add child_process types

* Fix synthetic defaults issue

* Remove docs

* Move scripts

* Run prettier

* Include examples in typechecking

* captureStackTrace types

* moved captureStackTrace types to globals

* Address reviews

Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2022-11-09 15:40:40 -08:00

789 lines
18 KiB
TypeScript

// @ts-nocheck
import { test, expect } from "bun:test";
test("decorator order of evaluation", () => {
let counter = 0;
const computedProp: unique symbol = Symbol("computedProp");
@decorator1
@decorator2
class BugReport {
@decorator7
type: string;
@decorator3
x: number = 20;
@decorator5
private _y: number = 12;
@decorator10
get y() {
return this._y;
}
@decorator11
set y(newY: number) {
this._y = newY;
}
@decorator9
[computedProp]: string = "yes";
constructor(@decorator8 type: string) {
this.type = type;
}
@decorator6
move(newX: number, @decorator12 newY: number) {
this.x = newX;
this._y = newY;
}
@decorator4
jump() {
this._y += 30;
}
}
function decorator1(target, propertyKey) {
expect(counter++).toBe(11);
expect(target === BugReport).toBe(true);
expect(propertyKey).toBe(undefined);
}
function decorator2(target, propertyKey) {
expect(counter++).toBe(10);
expect(target === BugReport).toBe(true);
expect(propertyKey).toBe(undefined);
}
function decorator3(target, propertyKey) {
expect(counter++).toBe(1);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("x");
}
function decorator4(target, propertyKey) {
expect(counter++).toBe(8);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("jump");
}
function decorator5(target, propertyKey) {
expect(counter++).toBe(2);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("_y");
}
function decorator6(target, propertyKey) {
expect(counter++).toBe(7);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("move");
}
function decorator7(target, propertyKey) {
expect(counter++).toBe(0);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("type");
}
function decorator8(target, propertyKey) {
expect(counter++).toBe(9);
expect(target === BugReport).toBe(true);
expect(propertyKey).toBe(undefined);
}
function decorator9(target, propertyKey) {
expect(counter++).toBe(5);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe(computedProp);
}
function decorator10(target, propertyKey) {
expect(counter++).toBe(3);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("y");
}
function decorator11(target, propertyKey) {
expect(counter++).toBe(4);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("y");
}
function decorator12(target, propertyKey) {
expect(counter++).toBe(6);
expect(target === BugReport.prototype).toBe(true);
expect(propertyKey).toBe("move");
}
});
test("decorator factories order of evaluation", () => {
let counter = 0;
const computedProp: unique symbol = Symbol("computedProp");
@decorator1()
@decorator2()
class BugReport {
@decorator7()
type: string;
@decorator3()
x: number = 20;
@decorator5()
private _y: number = 12;
@decorator10()
get y() {
return this._y;
}
@decorator11()
set y(newY: number) {
this._y = newY;
}
@decorator9()
[computedProp]: string = "yes";
constructor(@decorator8() type: string) {
this.type = type;
}
@decorator6()
move(newX: number, @decorator12() newY: number) {
this.x = newX;
this._y = newY;
}
@decorator4()
jump() {
this._y += 30;
}
}
function decorator1() {
expect(counter++).toBe(18);
return function (target, descriptorKey) {
expect(counter++).toBe(23);
};
}
function decorator2() {
expect(counter++).toBe(19);
return function (target, descriptorKey) {
expect(counter++).toBe(22);
};
}
function decorator3() {
expect(counter++).toBe(2);
return function (target, descriptorKey) {
expect(counter++).toBe(3);
};
}
function decorator4() {
expect(counter++).toBe(16);
return function (target, descriptorKey) {
expect(counter++).toBe(17);
};
}
function decorator5() {
expect(counter++).toBe(4);
return function (target, descriptorKey) {
expect(counter++).toBe(5);
};
}
function decorator6() {
expect(counter++).toBe(12);
return function (target, descriptorKey) {
expect(counter++).toBe(15);
};
}
function decorator7() {
expect(counter++).toBe(0);
return function (target, descriptorKey) {
expect(counter++).toBe(1);
};
}
function decorator8() {
expect(counter++).toBe(20);
return function (target, descriptorKey) {
expect(counter++).toBe(21);
};
}
function decorator9() {
expect(counter++).toBe(10);
return function (target, descriptorKey) {
expect(counter++).toBe(11);
};
}
function decorator10() {
expect(counter++).toBe(6);
return function (target, descriptorKey) {
expect(counter++).toBe(7);
};
}
function decorator11() {
expect(counter++).toBe(8);
return function (target, descriptorKey) {
expect(counter++).toBe(9);
};
}
function decorator12() {
expect(counter++).toBe(13);
return function (target, descriptorKey) {
expect(counter++).toBe(14);
};
}
});
test("parameter decorators", () => {
let counter = 0;
class HappyDecorator {
width: number;
height: number;
x: number;
y: number;
move(@d4 x: number, @d5 @d6 y: number) {
this.x = x;
this.y = y;
}
constructor(
one: number,
two: string,
three: boolean,
@d1 @d2 width: number,
@d3 height: number,
) {
this.width = width;
this.height = height;
}
dance(@d7 @d8 intensity: number) {
this.width *= intensity;
this.height *= intensity;
}
}
function d1(target, propertyKey, parameterIndex) {
expect(counter++).toBe(7);
expect(target === HappyDecorator).toBe(true);
expect(propertyKey).toBe(undefined);
expect(parameterIndex).toBe(3);
}
function d2(target, propertyKey, parameterIndex) {
expect(counter++).toBe(6);
expect(target === HappyDecorator).toBe(true);
expect(propertyKey).toBe(undefined);
expect(parameterIndex).toBe(3);
}
function d3(target, propertyKey, parameterIndex) {
expect(counter++).toBe(5);
expect(target === HappyDecorator).toBe(true);
expect(propertyKey).toBe(undefined);
expect(parameterIndex).toBe(4);
}
function d4(target, propertyKey, parameterIndex) {
expect(counter++).toBe(2);
expect(target === HappyDecorator.prototype).toBe(true);
expect(propertyKey).toBe("move");
expect(parameterIndex).toBe(0);
}
function d5(target, propertyKey, parameterIndex) {
expect(counter++).toBe(1);
expect(target === HappyDecorator.prototype).toBe(true);
expect(propertyKey).toBe("move");
expect(parameterIndex).toBe(1);
}
function d6(target, propertyKey, parameterIndex) {
expect(counter++).toBe(0);
expect(target === HappyDecorator.prototype).toBe(true);
expect(propertyKey).toBe("move");
expect(parameterIndex).toBe(1);
}
function d7(target, propertyKey, parameterIndex) {
expect(counter++).toBe(4);
expect(target === HappyDecorator.prototype).toBe(true);
expect(propertyKey).toBe("dance");
expect(parameterIndex).toBe(0);
}
function d8(target, propertyKey, parameterIndex) {
expect(counter++).toBe(3);
expect(target === HappyDecorator.prototype).toBe(true);
expect(propertyKey).toBe("dance");
expect(parameterIndex).toBe(0);
}
class Maybe {
constructor(
@m1 private x: number,
@m2 public y: boolean,
@m3 protected z: string,
) {}
}
function m1(target, propertyKey, index) {
expect(target === Maybe).toBe(true);
expect(propertyKey).toBe(undefined);
expect(index).toBe(0);
}
function m2(target, propertyKey, index) {
expect(target === Maybe).toBe(true);
expect(propertyKey).toBe(undefined);
expect(index).toBe(1);
}
function m3(target, propertyKey, index) {
expect(target === Maybe).toBe(true);
expect(propertyKey).toBe(undefined);
expect(index).toBe(2);
}
});
test("decorators random", () => {
@Frozen
class IceCream {}
function Frozen(constructor: Function) {
Object.freeze(constructor);
Object.freeze(constructor.prototype);
}
expect(Object.isFrozen(IceCream)).toBe(true);
class IceCreamComponent {
@Emoji()
flavor = "vanilla";
}
// Property Decorator
function Emoji() {
return function (target: Object, key: string | symbol) {
let val = target[key];
const getter = () => {
return val;
};
const setter = (next) => {
val = `🍦 ${next} 🍦`;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
const iceCream = new IceCreamComponent();
expect(iceCream.flavor === "🍦 vanilla 🍦").toBe(true);
iceCream.flavor = "chocolate";
expect(iceCream.flavor === "🍦 chocolate 🍦").toBe(true);
const i: unique symbol = Symbol.for("i");
const h: unique symbol = Symbol.for("h");
const t: unique symbol = Symbol.for("t");
const q: unique symbol = Symbol.for("q");
const p: unique symbol = Symbol.for("p");
const u3: unique symbol = Symbol.for("u3");
const u5: unique symbol = Symbol.for("u5");
const u6: unique symbol = Symbol.for("u6");
const u8: unique symbol = Symbol.for("u8");
class S {
@StringAppender("😛") k = 35;
@StringAppender("🤠") static j = 4;
@StringAppender("😵‍💫") private static [h] = 30;
@StringAppender("🤯") private static u = 60;
@StringAppender("🤪") private [t] = 32;
@StringAppender("🤑") [i] = 8;
@StringAppender("🎃") private e = 10;
@StringAppender("👻") static [q] = 202;
@StringAppender("😇") r = S[h];
_y: number;
@StringAppender("🤡") get y() {
return this._y;
}
set y(next) {
this._y = next;
}
#o = 100;
@StringAppender("😍") u1: number;
@StringAppender("🥳") static u2: number;
@StringAppender("🤓") private static [u3]: number;
@StringAppender("🥺") private static u4: number;
@StringAppender("🤯") private [u5]: number;
@StringAppender("🤩") [u6]: number;
@StringAppender("☹️") private u7: number;
@StringAppender("🙃") static [u8]: number;
@StringAppender("🤔") u9 = this.u1;
@StringAppender("🤨") u10 = this.u2;
@StringAppender("🙂") u11 = S[u3];
@StringAppender("🙁") u12 = S.u4;
@StringAppender("😐") u13 = this[u5];
@StringAppender("😑") u14 = this[u6];
@StringAppender("😶") u15 = this.u7;
@StringAppender("😏") u16 = S[u8];
constructor() {
this.k = 3;
expect(this.k).toBe("3 😛");
expect(S.j).toBe(4);
expect(this[i]).toBe("8 🤑");
expect(this.e).toBe("10 🎃");
expect(S[h]).toBe(30);
expect(S.u).toBe(60);
expect(this[t]).toBe("32 🤪");
expect(S[q]).toBe(202);
expect(this.#o).toBe(100);
expect(this.r).toBe("30 😇");
expect(this.y).toBe(undefined);
this.y = 100;
expect(this.y).toBe(100);
expect(this.u1).toBe(undefined);
expect(S.u2).toBe(undefined);
expect(S[u3]).toBe(undefined);
expect(S.u4).toBe(undefined);
expect(this[u5]).toBe(undefined);
expect(this[u6]).toBe(undefined);
expect(this.u7).toBe(undefined);
expect(S[u8]).toBe(undefined);
expect(this.u9).toBe("undefined 🤔");
expect(this.u10).toBe("undefined 🤨");
expect(this.u11).toBe("undefined 🙂");
expect(this.u12).toBe("undefined 🙁");
expect(this.u13).toBe("undefined 😐");
expect(this.u14).toBe("undefined 😑");
expect(this.u15).toBe("undefined 😶");
expect(this.u16).toBe("undefined 😏");
this.u1 = 100;
expect(this.u1).toBe("100 😍");
S.u2 = 100;
expect(S.u2).toBe("100 🥳");
S[u3] = 100;
expect(S[u3]).toBe("100 🤓");
S.u4 = 100;
expect(S.u4).toBe("100 🥺");
this[u5] = 100;
expect(this[u5]).toBe("100 🤯");
this[u6] = 100;
expect(this[u6]).toBe("100 🤩");
this.u7 = 100;
expect(this.u7).toBe("100 ☹️");
S[u8] = 100;
expect(S[u8]).toBe("100 🙃");
expect(this.u9).toBe("undefined 🤔");
expect(this.u10).toBe("undefined 🤨");
expect(this.u11).toBe("undefined 🙂");
expect(this.u12).toBe("undefined 🙁");
expect(this.u13).toBe("undefined 😐");
expect(this.u14).toBe("undefined 😑");
expect(this.u15).toBe("undefined 😶");
expect(this.u16).toBe("undefined 😏");
}
}
let s = new S();
expect(s.u9).toBe("undefined 🤔");
expect(s.u10).toBe("undefined 🤨");
expect(s.u11).toBe("undefined 🙂");
expect(s.u12).toBe("undefined 🙁");
expect(s.u13).toBe("undefined 😐");
expect(s.u14).toBe("undefined 😑");
expect(s.u15).toBe("undefined 😶");
expect(s.u16).toBe("undefined 😏");
s.u9 = 35;
expect(s.u9).toBe("35 🤔");
s.u10 = 36;
expect(s.u10).toBe("36 🤨");
s.u11 = 37;
expect(s.u11).toBe("37 🙂");
s.u12 = 38;
expect(s.u12).toBe("38 🙁");
s.u13 = 39;
expect(s.u13).toBe("39 😐");
s.u14 = 40;
expect(s.u14).toBe("40 😑");
s.u15 = 41;
expect(s.u15).toBe("41 😶");
s.u16 = 42;
expect(s.u16).toBe("42 😏");
function StringAppender(emoji: string) {
return function (target: Object, key: string | symbol) {
let val = target[key];
const getter = () => {
return val;
};
const setter = (value) => {
val = `${value} ${emoji}`;
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
});
test("class field order", () => {
class N {
l = 455;
}
class M {
u = 4;
@d1 w = 9;
constructor() {
// this.w = 9 should be moved here
expect(this.u).toBe(4);
expect(this.w).toBe(9);
this.u = 3;
this.w = 6;
expect(this.u).toBe(3);
expect(this.w).toBe(6);
}
}
function d1(target, propertyKey) {
expect(target === M.prototype).toBe(true);
expect(propertyKey).toBe("w");
}
let m = new M();
expect(m.u).toBe(3);
expect(m.w).toBe(6);
});
test("changing static method", () => {
class A {
static bar() {
return 1;
}
}
@changeMethodReturn("bar", 5)
class A_2 {
static bar() {
return 7;
}
}
function changeMethodReturn(method, value) {
return function (target) {
target[method] = function () {
return value;
};
return target;
};
}
@changeMethodReturn("bar", 2)
class B extends A {}
@changeMethodReturn("bar", 9)
class C extends B {}
expect(A_2.bar()).toBe(5);
expect(A.bar()).toBe(1);
expect(B.bar()).toBe(2);
expect(C.bar()).toBe(9);
});
test("class extending from another class", () => {
class A {
a: number;
constructor() {
this.a = 3;
}
}
class B extends A {
a: number = 9;
}
expect(new A().a).toBe(3);
expect(new B().a).toBe(9);
class C {
a: number = 80;
}
class D extends C {
a: number = 32;
constructor() {
super();
}
}
expect(new C().a).toBe(80);
expect(new D().a).toBe(32);
class E {
a: number = 40;
constructor() {
expect(this.a).toBe(40);
}
}
class F extends E {
@d1 a: number = 50;
constructor() {
super();
expect(this.a).toBe(50);
this.a = 60;
expect(this.a).toBe(60);
}
}
function d1(target) {
target.a = 100;
}
});
test("decorated fields moving to constructor", () => {
class A {
@d1 a = 3;
@d2 b = 4;
@d3 c = 5;
}
function d1(target, propertyKey) {
expect(target === A.prototype).toBe(true);
expect(propertyKey).toBe("a");
}
function d2(target, propertyKey) {
expect(target === A.prototype).toBe(true);
expect(propertyKey).toBe("b");
}
function d3(target, propertyKey) {
expect(target === A.prototype).toBe(true);
expect(propertyKey).toBe("c");
}
let a = new A();
expect(a.a).toBe(3);
expect(a.b).toBe(4);
expect(a.c).toBe(5);
});
test("only class decorator", () => {
let a = 0;
@d1
class A {}
let aa = new A();
function d1(target) {
a = 1;
expect(target).toBe(A);
}
expect(a).toBe(1);
});
test("only property decorators", () => {
let a = 0;
class A {
@d1 a() {}
}
let b = 0;
class B {
@d2 b = 3;
}
let c = 0;
class C {
@d3 get c() {
return 3;
}
}
function d1(target, propertyKey) {
a = 1;
expect(target === A.prototype).toBe(true);
expect(propertyKey).toBe("a");
}
expect(a).toBe(1);
function d2(target, propertyKey) {
b = 1;
expect(target === B.prototype).toBe(true);
expect(propertyKey).toBe("b");
}
expect(b).toBe(1);
function d3(target, propertyKey) {
c = 1;
expect(target === C.prototype).toBe(true);
expect(propertyKey).toBe("c");
}
expect(c).toBe(1);
});
test("only argument decorators", () => {
let a = 0;
class A {
a(@d1 a: string) {}
}
function d1(target, propertyKey, parameterIndex) {
a = 1;
expect(target === A.prototype).toBe(true);
expect(propertyKey).toBe("a");
expect(parameterIndex).toBe(0);
}
expect(a).toBe(1);
});
test("no decorators", () => {
let a = 0;
class A {
b: number;
constructor() {
a = 1;
this.b = 300000;
}
}
let aa = new A();
expect(a).toBe(1);
expect(aa.b).toBe(300000);
});