@@ -42,6 +42,40 @@ export interface PropertyOptions {
42
42
notify ?: boolean ;
43
43
reflectToAttribute ?: boolean ;
44
44
readOnly ?: boolean ;
45
+ computed ?: string ;
46
+ }
47
+
48
+ function createProperty ( proto : any , name : string , options ?: PropertyOptions ) : void {
49
+ const notify : boolean = options && options . notify || false ;
50
+ const reflectToAttribute : boolean =
51
+ options && options . reflectToAttribute || false ;
52
+ const readOnly : boolean = options && options . readOnly || false ;
53
+ const computed : string | undefined = options && options . computed || undefined ;
54
+
55
+ let type ;
56
+ if ( options && options . hasOwnProperty ( 'type' ) ) {
57
+ type = options . type ;
58
+ } else if (
59
+ ( window as any ) . Reflect && Reflect . hasMetadata && Reflect . getMetadata &&
60
+ Reflect . hasMetadata ( 'design:type' , proto , name ) ) {
61
+ type = Reflect . getMetadata ( 'design:type' , proto , name ) ;
62
+ } else {
63
+ console . error (
64
+ 'A type could not be found for ${propName}. ' +
65
+ 'Set a type or configure Metadata Reflection API support.' ) ;
66
+ }
67
+
68
+ if ( ! proto . constructor . hasOwnProperty ( 'properties' ) ) {
69
+ proto . constructor . properties = { } ;
70
+ }
71
+
72
+ proto . constructor . properties [ name ] = {
73
+ type,
74
+ notify,
75
+ reflectToAttribute,
76
+ readOnly,
77
+ computed
78
+ } ;
45
79
}
46
80
47
81
/**
@@ -52,33 +86,7 @@ export interface PropertyOptions {
52
86
*/
53
87
export function property ( options ?: PropertyOptions ) {
54
88
return ( proto : any , propName : string ) : any => {
55
- const notify : boolean = options && options . notify || false ;
56
- const reflectToAttribute : boolean =
57
- options && options . reflectToAttribute || false ;
58
- const readOnly : boolean = options && options . readOnly || false ;
59
-
60
- let type ;
61
- if ( options && options . hasOwnProperty ( 'type' ) ) {
62
- type = options . type ;
63
- } else if (
64
- ( window as any ) . Reflect && Reflect . hasMetadata && Reflect . getMetadata &&
65
- Reflect . hasMetadata ( 'design:type' , proto , propName ) ) {
66
- type = Reflect . getMetadata ( 'design:type' , proto , propName ) ;
67
- } else {
68
- console . error (
69
- 'A type could not be found for ${propName}. ' +
70
- 'Set a type or configure Metadata Reflection API support.' ) ;
71
- }
72
-
73
- if ( ! proto . constructor . hasOwnProperty ( 'properties' ) ) {
74
- proto . constructor . properties = { } ;
75
- }
76
- proto . constructor . properties [ propName ] = {
77
- type,
78
- notify,
79
- reflectToAttribute,
80
- readOnly,
81
- } ;
89
+ createProperty ( proto , propName , options ) ;
82
90
}
83
91
}
84
92
@@ -100,6 +108,29 @@ export function observe(targets: string|string[]) {
100
108
}
101
109
}
102
110
111
+ /**
112
+ * A TypeScript accessor decorator factory that causes the decorated accessor to
113
+ * be called when a property changes. `targets` is either a single property
114
+ * name, or a list of property names.
115
+ *
116
+ * This function must be invoked to return a decorator.
117
+ */
118
+ export function computed < T = any > ( ...targets : ( keyof T ) [ ] ) {
119
+ return ( proto : any , propName : string , descriptor : PropertyDescriptor ) : void => {
120
+ const targetString = targets . join ( ',' ) ;
121
+ const propNameCased = propName [ 0 ] . toUpperCase ( ) + propName . slice ( 1 ) ;
122
+ const fnName = `__compute${ propNameCased } ` ;
123
+
124
+ proto . constructor . prototype [ fnName ] = descriptor . get ;
125
+
126
+ descriptor . get = undefined ;
127
+
128
+ createProperty ( proto , propName , {
129
+ computed : `${ fnName } (${ targetString } )`
130
+ } ) ;
131
+ } ;
132
+ }
133
+
103
134
/**
104
135
* A TypeScript property decorator factory that converts a class property into
105
136
* a getter that executes a querySelector on the element's shadow root.
0 commit comments