245 lines
4.6 KiB
TypeScript
245 lines
4.6 KiB
TypeScript
// @declaration: true
|
|
// @strict: true
|
|
// @target: es5
|
|
|
|
// In methods of an object literal with no contextual type, 'this' has the type
|
|
// of the object literal.
|
|
|
|
let obj1 = {
|
|
a: 1,
|
|
f() {
|
|
return this.a;
|
|
},
|
|
b: "hello",
|
|
c: {
|
|
g() {
|
|
this.g();
|
|
}
|
|
},
|
|
get d() {
|
|
return this.a;
|
|
},
|
|
get e() {
|
|
return this.b;
|
|
},
|
|
set e(value) {
|
|
this.b = value;
|
|
}
|
|
};
|
|
|
|
// In methods of an object literal with a contextual type, 'this' has the
|
|
// contextual type.
|
|
|
|
type Point = {
|
|
x: number;
|
|
y: number;
|
|
z?: number;
|
|
moveBy(dx: number, dy: number, dz?: number): void;
|
|
}
|
|
|
|
let p1: Point = {
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
};
|
|
|
|
let p2: Point | null = {
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
};
|
|
|
|
let p3: Point | undefined = {
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
};
|
|
|
|
let p4: Point | null | undefined = {
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
};
|
|
|
|
declare function f1(p: Point): void;
|
|
|
|
f1({
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
});
|
|
|
|
declare function f2(p: Point | null | undefined): void;
|
|
|
|
f2({
|
|
x: 10,
|
|
y: 20,
|
|
moveBy(dx, dy, dz) {
|
|
this.x += dx;
|
|
this.y += dy;
|
|
if (this.z && dz) {
|
|
this.z += dz;
|
|
}
|
|
}
|
|
});
|
|
|
|
// In methods of an object literal with a contextual type that includes some
|
|
// ThisType<T>, 'this' is of type T.
|
|
|
|
type ObjectDescriptor<D, M> = {
|
|
data?: D;
|
|
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
|
|
}
|
|
|
|
declare function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M;
|
|
|
|
let x1 = makeObject({
|
|
data: { x: 0, y: 0 },
|
|
methods: {
|
|
moveBy(dx: number, dy: number) {
|
|
this.x += dx; // Strongly typed this
|
|
this.y += dy; // Strongly typed this
|
|
}
|
|
}
|
|
});
|
|
|
|
// In methods contained in an object literal with a contextual type that includes
|
|
// some ThisType<T>, 'this' is of type T.
|
|
|
|
type ObjectDescriptor2<D, M> = ThisType<D & M> & {
|
|
data?: D;
|
|
methods?: M;
|
|
}
|
|
|
|
declare function makeObject2<D, M>(desc: ObjectDescriptor<D, M>): D & M;
|
|
|
|
let x2 = makeObject2({
|
|
data: { x: 0, y: 0 },
|
|
methods: {
|
|
moveBy(dx: number, dy: number) {
|
|
this.x += dx; // Strongly typed this
|
|
this.y += dy; // Strongly typed this
|
|
}
|
|
}
|
|
});
|
|
|
|
// Check pattern similar to Object.defineProperty and Object.defineProperties
|
|
|
|
type PropDesc<T> = {
|
|
value?: T;
|
|
get?(): T;
|
|
set?(value: T): void;
|
|
}
|
|
|
|
type PropDescMap<T> = {
|
|
[K in keyof T]: PropDesc<T[K]>;
|
|
}
|
|
|
|
declare function defineProp<T, K extends string, U>(obj: T, name: K, desc: PropDesc<U> & ThisType<T>): T & Record<K, U>;
|
|
|
|
declare function defineProps<T, U>(obj: T, descs: PropDescMap<U> & ThisType<T>): T & U;
|
|
|
|
let p10 = defineProp(p1, "foo", { value: 42 });
|
|
p10.foo = p10.foo + 1;
|
|
|
|
let p11 = defineProp(p1, "bar", {
|
|
get() {
|
|
return this.x;
|
|
},
|
|
set(value: number) {
|
|
this.x = value;
|
|
}
|
|
});
|
|
p11.bar = p11.bar + 1;
|
|
|
|
let p12 = defineProps(p1, {
|
|
foo: {
|
|
value: 42
|
|
},
|
|
bar: {
|
|
get(): number {
|
|
return this.x;
|
|
},
|
|
set(value: number) {
|
|
this.x = value;
|
|
}
|
|
}
|
|
});
|
|
p12.foo = p12.foo + 1;
|
|
p12.bar = p12.bar + 1;
|
|
|
|
// Proof of concept for typing of Vue.js
|
|
|
|
type Accessors<T> = { [K in keyof T]: (() => T[K]) | Computed<T[K]> };
|
|
|
|
type Dictionary<T> = { [x: string]: T }
|
|
|
|
type Computed<T> = {
|
|
get?(): T;
|
|
set?(value: T): void;
|
|
}
|
|
|
|
type VueOptions<D, M, P> = ThisType<D & M & P> & {
|
|
data?: D | (() => D);
|
|
methods?: M;
|
|
computed?: Accessors<P>;
|
|
}
|
|
|
|
declare const Vue: new <D, M, P>(options: VueOptions<D, M, P>) => D & M & P;
|
|
|
|
let vue = new Vue({
|
|
data: () => ({ x: 1, y: 2 }),
|
|
methods: {
|
|
f(x: string) {
|
|
return this.x;
|
|
}
|
|
},
|
|
computed: {
|
|
test(): number {
|
|
return this.x;
|
|
},
|
|
hello: {
|
|
get() {
|
|
return "hi";
|
|
},
|
|
set(value: string) {
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
vue;
|
|
vue.x;
|
|
vue.f("abc");
|
|
vue.test;
|
|
vue.hello;
|