Skip to content

Commit e396ec3

Browse files
authored
Remove almost all uses of any. (#457)
* Remove almost all uses of `any`. Also marks any properties that seem readonly as `readonly`. Likewise `ReadonlyArray`. attributeChangedCallback can get null values for removed attributes.
1 parent daf5276 commit e396ec3

9 files changed

+54
-26
lines changed

src/lib/decorators.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {LitElement} from '../lit-element.js';
1818
import {PropertyDeclaration, UpdatingElement} from './updating-element.js';
1919

2020
export type Constructor<T> = {
21-
new (...args: any[]): T
21+
new (...args: unknown[]): T
2222
};
2323

2424
// From the TC39 Decorators proposal
@@ -47,6 +47,7 @@ const legacyCustomElement =
4747
// `Constructor<HTMLElement>` for some reason.
4848
// `Constructor<HTMLElement>` is helpful to make sure the decorator is
4949
// applied to elements however.
50+
// tslint:disable-next-line:no-any
5051
return clazz as any;
5152
};
5253

@@ -106,6 +107,7 @@ const standardProperty =
106107
// initializer: descriptor.initializer,
107108
// }
108109
// ],
110+
// tslint:disable-next-line:no-any decorator
109111
initializer(this: any) {
110112
if (typeof element.initializer === 'function') {
111113
this[element.key] = element.initializer!.call(this);
@@ -132,6 +134,7 @@ const legacyProperty =
132134
* @ExportDecoratedItems
133135
*/
134136
export function property(options?: PropertyDeclaration) {
137+
// tslint:disable-next-line:no-any decorator
135138
return (protoOrDescriptor: Object|ClassElement, name?: PropertyKey): any =>
136139
(name !== undefined) ?
137140
legacyProperty(options!, protoOrDescriptor as Object, name) :
@@ -177,6 +180,7 @@ const standardQuery = (descriptor: PropertyDescriptor, element: ClassElement) =>
177180
function _query<T>(queryFn: (target: NodeSelector, selector: string) => T) {
178181
return (selector: string) =>
179182
(protoOrDescriptor: Object|ClassElement,
183+
// tslint:disable-next-line:no-any decorator
180184
name?: PropertyKey): any => {
181185
const descriptor = {
182186
get(this: LitElement) {
@@ -203,6 +207,7 @@ const standardEventOptions =
203207
};
204208

205209
const legacyEventOptions =
210+
// tslint:disable-next-line:no-any legacy decorator
206211
(options: AddEventListenerOptions, proto: any, name: PropertyKey) => {
207212
Object.assign(proto[name], options);
208213
};
@@ -243,4 +248,5 @@ export const eventOptions = (options: AddEventListenerOptions) =>
243248
(name !== undefined) ?
244249
legacyEventOptions(options, protoOrDescriptor as Object, name) :
245250
standardEventOptions(options, protoOrDescriptor as ClassElement)) as
246-
any;
251+
// tslint:disable-next-line:no-any decorator
252+
any;

src/lib/updating-element.ts

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,42 +33,45 @@ declare global {
3333
/**
3434
* Converts property values to and from attribute values.
3535
*/
36-
export interface ComplexAttributeConverter<Type = any, TypeHint = any> {
36+
export interface ComplexAttributeConverter<Type = unknown, TypeHint = unknown> {
3737
/**
3838
* Function called to convert an attribute value to a property
3939
* value.
4040
*/
41-
fromAttribute?(value: string, type?: TypeHint): Type;
41+
fromAttribute?(value: string|null, type?: TypeHint): Type;
4242

4343
/**
4444
* Function called to convert a property value to an attribute
4545
* value.
46+
*
47+
* It returns unknown instead of string, to be compatible with
48+
* https://github.com/WICG/trusted-types (and similar efforts).
4649
*/
47-
toAttribute?(value: Type, type?: TypeHint): string|null;
50+
toAttribute?(value: Type, type?: TypeHint): unknown;
4851
}
4952

50-
type AttributeConverter<Type = any, TypeHint = any> =
53+
type AttributeConverter<Type = unknown, TypeHint = unknown> =
5154
ComplexAttributeConverter<Type>|((value: string, type?: TypeHint) => Type);
5255

5356
/**
5457
* Defines options for a property accessor.
5558
*/
56-
export interface PropertyDeclaration<Type = any, TypeHint = any> {
59+
export interface PropertyDeclaration<Type = unknown, TypeHint = unknown> {
5760
/**
5861
* Indicates how and whether the property becomes an observed attribute.
5962
* If the value is `false`, the property is not added to `observedAttributes`.
6063
* If true or absent, the lowercased property name is observed (e.g. `fooBar`
6164
* becomes `foobar`). If a string, the string value is observed (e.g
6265
* `attribute: 'foo-bar'`).
6366
*/
64-
attribute?: boolean|string;
67+
readonly attribute?: boolean|string;
6568

6669
/**
6770
* Indicates the type of the property. This is used only as a hint for the
6871
* `converter` to determine how to convert the attribute
6972
* to/from a property.
7073
*/
71-
type?: TypeHint;
74+
readonly type?: TypeHint;
7275

7376
/**
7477
* Indicates how to convert the attribute to/from a property. If this value
@@ -82,7 +85,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
8285
* the property is never updated again as a result of the attribute changing,
8386
* and vice versa.
8487
*/
85-
converter?: AttributeConverter<Type, TypeHint>;
88+
readonly converter?: AttributeConverter<Type, TypeHint>;
8689

8790
/**
8891
* Indicates if the property should reflect to an attribute.
@@ -91,7 +94,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
9194
* property option and the value of the property converted using the rules
9295
* from the `converter` property option.
9396
*/
94-
reflect?: boolean;
97+
readonly reflect?: boolean;
9598

9699
/**
97100
* A function that indicates if a property should be considered changed when
@@ -108,7 +111,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
108111
* `this.requestUpdate(propertyName, oldValue)` to request an update when
109112
* the property changes.
110113
*/
111-
noAccessor?: boolean;
114+
readonly noAccessor?: boolean;
112115
}
113116

114117
/**
@@ -117,7 +120,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
117120
* PropertyDeclaration options.
118121
*/
119122
export interface PropertyDeclarations {
120-
[key: string]: PropertyDeclaration;
123+
readonly [key: string]: PropertyDeclaration;
121124
}
122125

123126
type PropertyDeclarationMap = Map<PropertyKey, PropertyDeclaration>;
@@ -128,7 +131,7 @@ export type PropertyValues = Map<PropertyKey, unknown>;
128131

129132
export const defaultConverter: ComplexAttributeConverter = {
130133

131-
toAttribute(value: any, type?: any) {
134+
toAttribute(value: unknown, type?: unknown): unknown {
132135
switch (type) {
133136
case Boolean:
134137
return value ? '' : null;
@@ -141,15 +144,15 @@ export const defaultConverter: ComplexAttributeConverter = {
141144
return value;
142145
},
143146

144-
fromAttribute(value: any, type?: any) {
147+
fromAttribute(value: string|null, type?: unknown) {
145148
switch (type) {
146149
case Boolean:
147150
return value !== null;
148151
case Number:
149152
return value === null ? null : Number(value);
150153
case Object:
151154
case Array:
152-
return JSON.parse(value);
155+
return JSON.parse(value!);
153156
}
154157
return value;
155158
}
@@ -256,10 +259,12 @@ export abstract class UpdatingElement extends HTMLElement {
256259
JSCompiler_renameProperty('_classProperties', this))) {
257260
this._classProperties = new Map();
258261
// NOTE: Workaround IE11 not supporting Map constructor argument.
259-
const superProperties = Object.getPrototypeOf(this)._classProperties;
262+
const superProperties: PropertyDeclarationMap =
263+
Object.getPrototypeOf(this)._classProperties;
260264
if (superProperties !== undefined) {
261265
superProperties.forEach(
262-
(v: any, k: PropertyKey) => this._classProperties!.set(k, v));
266+
(v: PropertyDeclaration, k: PropertyKey) =>
267+
this._classProperties!.set(k, v));
263268
}
264269
}
265270
}
@@ -289,11 +294,15 @@ export abstract class UpdatingElement extends HTMLElement {
289294
}
290295
const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
291296
Object.defineProperty(this.prototype, name, {
297+
// tslint:disable-next-line:no-any no symbol in index
292298
get(): any {
299+
// tslint:disable-next-line:no-any no symbol in index
293300
return (this as any)[key];
294301
},
295-
set(this: UpdatingElement, value: any) {
302+
set(this: UpdatingElement, value: unknown) {
303+
// tslint:disable-next-line:no-any no symbol in index
296304
const oldValue = (this as any)[name];
305+
// tslint:disable-next-line:no-any no symbol in index
297306
(this as any)[key] = value;
298307
this.requestUpdate(name, oldValue);
299308
},
@@ -338,6 +347,7 @@ export abstract class UpdatingElement extends HTMLElement {
338347
for (const p of propKeys) {
339348
// note, use of `any` is due to TypeSript lack of support for symbol in
340349
// index types
350+
// tslint:disable-next-line:no-any no symbol in index
341351
this.createProperty(p, (props as any)[p]);
342352
}
343353
}
@@ -375,7 +385,7 @@ export abstract class UpdatingElement extends HTMLElement {
375385
* @nocollapse
376386
*/
377387
private static _propertyValueFromAttribute(
378-
value: string, options: PropertyDeclaration) {
388+
value: string|null, options: PropertyDeclaration) {
379389
const type = options.type;
380390
const converter = options.converter || defaultConverter;
381391
const fromAttribute =
@@ -468,6 +478,7 @@ export abstract class UpdatingElement extends HTMLElement {
468478
private _applyInstanceProperties() {
469479
// Use forEach so this works even if for/of loops are compiled to for loops
470480
// expecting arrays
481+
// tslint:disable-next-line:no-any
471482
this._instanceProperties!.forEach((v, p) => (this as any)[p] = v);
472483
this._instanceProperties = undefined;
473484
}
@@ -497,7 +508,7 @@ export abstract class UpdatingElement extends HTMLElement {
497508
/**
498509
* Synchronizes property values when attributes change.
499510
*/
500-
attributeChangedCallback(name: string, old: string, value: string) {
511+
attributeChangedCallback(name: string, old: string|null, value: string|null) {
501512
if (old !== value) {
502513
this._attributeToProperty(name, value);
503514
}
@@ -526,14 +537,14 @@ export abstract class UpdatingElement extends HTMLElement {
526537
if (attrValue == null) {
527538
this.removeAttribute(attr);
528539
} else {
529-
this.setAttribute(attr, attrValue);
540+
this.setAttribute(attr, attrValue as string);
530541
}
531542
// mark state not reflecting
532543
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE;
533544
}
534545
}
535546

536-
private _attributeToProperty(name: string, value: string) {
547+
private _attributeToProperty(name: string, value: string|null) {
537548
// Use tracking info to avoid deserializing attribute value if it was
538549
// just set from a property setter.
539550
if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) {
@@ -547,7 +558,8 @@ export abstract class UpdatingElement extends HTMLElement {
547558
// mark state reflecting
548559
this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY;
549560
this[propName as keyof this] =
550-
ctor._propertyValueFromAttribute(value, options);
561+
// tslint:disable-next-line:no-any
562+
ctor._propertyValueFromAttribute(value, options) as any;
551563
// mark state not reflecting
552564
this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
553565
}
@@ -566,7 +578,7 @@ export abstract class UpdatingElement extends HTMLElement {
566578
* @param oldValue {any} (optional) old value of requesting property
567579
* @returns {Promise} A Promise that is resolved when the update completes.
568580
*/
569-
requestUpdate(name?: PropertyKey, oldValue?: any) {
581+
requestUpdate(name?: PropertyKey, oldValue?: unknown) {
570582
let shouldRequestUpdate = true;
571583
// if we have a property key, perform property update steps.
572584
if (name !== undefined && !this._changedProperties.has(name)) {

src/lit-element.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class LitElement extends UpdatingElement {
208208
*/
209209
protected update(changedProperties: PropertyValues) {
210210
super.update(changedProperties);
211-
const templateResult = this.render() as any;
211+
const templateResult = this.render() as unknown;
212212
if (templateResult instanceof TemplateResult) {
213213
(this.constructor as typeof LitElement)
214214
.render(

src/test/lib/decorators_test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {eventOptions, property} from '../../lib/decorators.js';
1616
import {customElement, html, LitElement, PropertyValues, query, queryAll} from '../../lit-element.js';
1717
import {generateElementName} from '../test-helpers.js';
1818

19+
// tslint:disable:no-any ok in tests
20+
1921
let hasOptions;
2022
const supportsOptions = (function() {
2123
if (hasOptions !== undefined) {

src/test/lib/updating-element_test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {property} from '../../lib/decorators.js';
1616
import {ComplexAttributeConverter, PropertyDeclarations, PropertyValues, UpdatingElement} from '../../lib/updating-element.js';
1717
import {generateElementName} from '../test-helpers.js';
1818

19+
// tslint:disable:no-any ok in tests
20+
1921
const assert = chai.assert;
2022

2123
suite('UpdatingElement', () => {

src/test/lit-element_styling_test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ suite('Static get styles', () => {
456456

457457
test('`css` get styles throws when unsafe values are used', async () => {
458458
assert.throws(() => {
459+
// tslint:disable:no-any intentionally unsafe code
459460
css`div { border: ${`2px solid blue;` as any}}`;
460461
});
461462
});

src/test/lit-element_test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {generateElementName, stripExpressionDelimeters} from './test-helpers.js'
1818

1919
const assert = chai.assert;
2020

21+
// tslint:disable:no-any ok in tests
22+
2123
suite('LitElement', () => {
2224
let container: HTMLElement;
2325

tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"noUnusedParameters": true,
1414
"noImplicitReturns": true,
1515
"noFallthroughCasesInSwitch": true,
16+
"noImplicitAny": true,
17+
"noImplicitThis": true,
1618
"outDir": "./",
1719
// Only necessary because @types/uglify-js can't find types for source-map
1820
"skipLibCheck": true,

tslint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"spaces"
88
],
99
"prefer-const": true,
10+
"no-any": true,
1011
"no-duplicate-variable": true,
1112
"no-eval": true,
1213
"no-internal-module": true,

0 commit comments

Comments
 (0)