GeoScene API for JavaScript 使用 mixins 来构建其类。请阅读此优秀文章,深入探讨使用 TypeScript 的 mixins。
首先,定义 EventedMixin 以将事件系统添加到类中。
Defining an Accessor mixin - TSDefining an Accessor mixin - JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import Accessor = require("geoscene/core/Accessor");
import { subclass } from"geoscene/core/accessorSupport/decorators";
// A type to represent a constructor functiontype Constructor<T = object> = new (...args: any[]) => T;
// A type to represent a mixin function// See for more details https://www.bryntum.com/blog/the-mixin-pattern-in-typescript-all-you-need-to-know/type Mixin<T extends (...input: any[]) => any> = InstanceType<ReturnType<T>>;
// TBase extends Constructor<Accessor> indicates that `EventedMixin`// expects the base class to extend `Accessor`, for example to be able to use the `watch` method.exportconst EventedMixin = <TBase extends Constructor<Accessor>>(Base: TBase) => {
@subclass("geoscene.guide.Evented")
class Evented extends Base {
/**
* A first function defined by the mixin
*/
emit(type: string, event?: any): boolean {
// ...
}
/**
* Another function defined by the mixin
*/
on(type: string, listener: (event: any) => void): IHandle {
// ...
}
}
return Evented;
}
// define the type of the mixin. This is useful to type properties that extends this mixin
// eg: `myProperty: EventedMixin;`
export type EventedMixin = Mixin<typeof EventedMixin>;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import Accessor = require("geoscene/core/Accessor");
import { subclass, property } from"geoscene/core/accessorSupport/decorators";
@subclass("geoscene.guide.Collection")
classCollectionextendsAccessor{
private _items: any[] = [];
// Example: Define a custom property getter.// Accessor caches the values returned by the getters.// At this point `length` will never change.// See the "Notify a property change" section@property()
getlength(): number {
returnthis._items.length;
}
setlength(value: number) {
// Example: perform validationif (value <= 0) {
thrownewError(`value of length not valid: ${value}`);
}
// internally you can access the cached value of `length` using `_get`.const oldValue = this._get<number>("length");
if (oldValue !== value) {
// a setter has to update the value from the cachethis._set("length", value);
// Example: perform additional work when the length changesthis._items.length = value;
}
}
}
定义只读属性
以下语法显示如何设置只读属性。
Read-only - TSRead-only - JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Accessor = require("geoscene/core/Accessor");
import { subclass, property } from"geoscene/core/accessorSupport/decorators";
@subclass("geoscene.guide.Person")
classPersonextendsAccessor{
// Example: read-only property may not be externally set@property({ readOnly: true })
firstName: string;
@property({ readOnly: true })
lastName: string;
updateName(firstName: string, lastName: string): void {
// We may still update the read-only property internally, which will change// the property and notify changes to watchersthis._set({
firstName: firstName,
lastName: lastName
});
}
}
定义代理属性
有时,除了可能对值执行转换之外,您还需要在读取和写入时代理属性。例如,公开内部成员属性。
Proxy property - TSProxy property - JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Accessor = require("geoscene/core/Accessor");
import { subclass, aliasOf } from"geoscene/core/accessorSupport/decorators";
@subclass("geoscene.guide.GroupLayer")
classGroupLayerextendsAccessor{
@property()
sublayers: Collection = new Collection();
// Define a property that reflects one in another object.@property({ aliasOf: "sublayers.length" })
length: number;
// Alternatively you can use the `@aliasOf` decorator// @aliasOf// length: number// You can also proxy a method from another object.@aliasOf("sublayers.add")
add: (item: any) =>void;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Accessor = require("geoscene/core/Accessor");
import { subclass, property } from"geoscene/core/accessorSupport/decorators";
@subclass("geoscene.guide.Collection")
classCollectionextendsAccessor{
private _items: any[] = [];
@property({
readOnly: true })
getlength(): number {
returnthis._items.length;
}
add(item: any): void {
this._items.push(item);
// We know the value of `length` is changed.// Notify so that at next access, the getter will be invokedthis.notifyChange("length");
}
}
自动转换
定义属性类型
可以为类的属性定义类型。
Define the property type - TSDefine the property type - JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Graphic = require("geoscene/Graphic");
import Accessor = require("geoscene/core/Accessor");
import Collection = require("geoscene/core/Collection");
import { subclass, property } from"geoscene/core/accessorSupport/decorators";
@subclass()
classGraphicsLayerextendsAccessor{
@property({
// Define the type of the collection of Graphics// When the property is set with an array,// the collection constructor will automatically be calledtype: Collection.ofType(Graphic)
})
graphics: Collection<Graphic>;
}
Define a casting method - TSDefine a casting method - JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import Accessor = require("geoscene/core/Accessor");
import { subclass, property, cast } from"geoscene/core/tsSupport/declare";
@subclass()
classColorextendsAccessor{
@property()
r: number = 0;
@property()
g: number = 0;
@property()
b: number = 0;
@property()
a: number = 1;
@cast("r")
@cast("g")
@cast("b")
protected castComponent(value: number): number {
// cast method that clamp the value that// will be set on r, g or b between 0 and 255returnMath.max(0, Math.min(255, value));
}
@cast("a")
protected castAlpha(value: number): number {
// cast method that clamp the value that// will be set on a between 0 and 1returnMath.max(0, Math.min(1, value));
}
}