diff --git a/src/lib/decorators.ts b/src/lib/decorators.ts index bc72e65e..f5a2eb36 100644 --- a/src/lib/decorators.ts +++ b/src/lib/decorators.ts @@ -18,7 +18,7 @@ import {LitElement} from '../lit-element.js'; import {PropertyDeclaration, UpdatingElement} from './updating-element.js'; export type Constructor = { - new (...args: any[]): T + new (...args: unknown[]): T }; // From the TC39 Decorators proposal @@ -47,6 +47,7 @@ const legacyCustomElement = // `Constructor` for some reason. // `Constructor` is helpful to make sure the decorator is // applied to elements however. + // tslint:disable-next-line:no-any return clazz as any; }; @@ -106,6 +107,7 @@ const standardProperty = // initializer: descriptor.initializer, // } // ], + // tslint:disable-next-line:no-any decorator initializer(this: any) { if (typeof element.initializer === 'function') { this[element.key] = element.initializer!.call(this); @@ -132,6 +134,7 @@ const legacyProperty = * @ExportDecoratedItems */ export function property(options?: PropertyDeclaration) { + // tslint:disable-next-line:no-any decorator return (protoOrDescriptor: Object|ClassElement, name?: PropertyKey): any => (name !== undefined) ? legacyProperty(options!, protoOrDescriptor as Object, name) : @@ -177,6 +180,7 @@ const standardQuery = (descriptor: PropertyDescriptor, element: ClassElement) => function _query(queryFn: (target: NodeSelector, selector: string) => T) { return (selector: string) => (protoOrDescriptor: Object|ClassElement, + // tslint:disable-next-line:no-any decorator name?: PropertyKey): any => { const descriptor = { get(this: LitElement) { @@ -203,6 +207,7 @@ const standardEventOptions = }; const legacyEventOptions = + // tslint:disable-next-line:no-any legacy decorator (options: AddEventListenerOptions, proto: any, name: PropertyKey) => { Object.assign(proto[name], options); }; @@ -243,4 +248,5 @@ export const eventOptions = (options: AddEventListenerOptions) => (name !== undefined) ? legacyEventOptions(options, protoOrDescriptor as Object, name) : standardEventOptions(options, protoOrDescriptor as ClassElement)) as - any; + // tslint:disable-next-line:no-any decorator + any; diff --git a/src/lib/updating-element.ts b/src/lib/updating-element.ts index f753dc08..cd8ac8bc 100644 --- a/src/lib/updating-element.ts +++ b/src/lib/updating-element.ts @@ -33,27 +33,30 @@ declare global { /** * Converts property values to and from attribute values. */ -export interface ComplexAttributeConverter { +export interface ComplexAttributeConverter { /** * Function called to convert an attribute value to a property * value. */ - fromAttribute?(value: string, type?: TypeHint): Type; + fromAttribute?(value: string|null, type?: TypeHint): Type; /** * Function called to convert a property value to an attribute * value. + * + * It returns unknown instead of string, to be compatible with + * https://github.com/WICG/trusted-types (and similar efforts). */ - toAttribute?(value: Type, type?: TypeHint): string|null; + toAttribute?(value: Type, type?: TypeHint): unknown; } -type AttributeConverter = +type AttributeConverter = ComplexAttributeConverter|((value: string, type?: TypeHint) => Type); /** * Defines options for a property accessor. */ -export interface PropertyDeclaration { +export interface PropertyDeclaration { /** * Indicates how and whether the property becomes an observed attribute. * If the value is `false`, the property is not added to `observedAttributes`. @@ -61,14 +64,14 @@ export interface PropertyDeclaration { * becomes `foobar`). If a string, the string value is observed (e.g * `attribute: 'foo-bar'`). */ - attribute?: boolean|string; + readonly attribute?: boolean|string; /** * Indicates the type of the property. This is used only as a hint for the * `converter` to determine how to convert the attribute * to/from a property. */ - type?: TypeHint; + readonly type?: TypeHint; /** * Indicates how to convert the attribute to/from a property. If this value @@ -82,7 +85,7 @@ export interface PropertyDeclaration { * the property is never updated again as a result of the attribute changing, * and vice versa. */ - converter?: AttributeConverter; + readonly converter?: AttributeConverter; /** * Indicates if the property should reflect to an attribute. @@ -91,7 +94,7 @@ export interface PropertyDeclaration { * property option and the value of the property converted using the rules * from the `converter` property option. */ - reflect?: boolean; + readonly reflect?: boolean; /** * A function that indicates if a property should be considered changed when @@ -108,7 +111,7 @@ export interface PropertyDeclaration { * `this.requestUpdate(propertyName, oldValue)` to request an update when * the property changes. */ - noAccessor?: boolean; + readonly noAccessor?: boolean; } /** @@ -117,7 +120,7 @@ export interface PropertyDeclaration { * PropertyDeclaration options. */ export interface PropertyDeclarations { - [key: string]: PropertyDeclaration; + readonly [key: string]: PropertyDeclaration; } type PropertyDeclarationMap = Map; @@ -128,7 +131,7 @@ export type PropertyValues = Map; export const defaultConverter: ComplexAttributeConverter = { - toAttribute(value: any, type?: any) { + toAttribute(value: unknown, type?: unknown): unknown { switch (type) { case Boolean: return value ? '' : null; @@ -141,7 +144,7 @@ export const defaultConverter: ComplexAttributeConverter = { return value; }, - fromAttribute(value: any, type?: any) { + fromAttribute(value: string|null, type?: unknown) { switch (type) { case Boolean: return value !== null; @@ -149,7 +152,7 @@ export const defaultConverter: ComplexAttributeConverter = { return value === null ? null : Number(value); case Object: case Array: - return JSON.parse(value); + return JSON.parse(value!); } return value; } @@ -256,10 +259,12 @@ export abstract class UpdatingElement extends HTMLElement { JSCompiler_renameProperty('_classProperties', this))) { this._classProperties = new Map(); // NOTE: Workaround IE11 not supporting Map constructor argument. - const superProperties = Object.getPrototypeOf(this)._classProperties; + const superProperties: PropertyDeclarationMap = + Object.getPrototypeOf(this)._classProperties; if (superProperties !== undefined) { superProperties.forEach( - (v: any, k: PropertyKey) => this._classProperties!.set(k, v)); + (v: PropertyDeclaration, k: PropertyKey) => + this._classProperties!.set(k, v)); } } } @@ -289,11 +294,15 @@ export abstract class UpdatingElement extends HTMLElement { } const key = typeof name === 'symbol' ? Symbol() : `__${name}`; Object.defineProperty(this.prototype, name, { + // tslint:disable-next-line:no-any no symbol in index get(): any { + // tslint:disable-next-line:no-any no symbol in index return (this as any)[key]; }, - set(this: UpdatingElement, value: any) { + set(this: UpdatingElement, value: unknown) { + // tslint:disable-next-line:no-any no symbol in index const oldValue = (this as any)[name]; + // tslint:disable-next-line:no-any no symbol in index (this as any)[key] = value; this.requestUpdate(name, oldValue); }, @@ -338,6 +347,7 @@ export abstract class UpdatingElement extends HTMLElement { for (const p of propKeys) { // note, use of `any` is due to TypeSript lack of support for symbol in // index types + // tslint:disable-next-line:no-any no symbol in index this.createProperty(p, (props as any)[p]); } } @@ -375,7 +385,7 @@ export abstract class UpdatingElement extends HTMLElement { * @nocollapse */ private static _propertyValueFromAttribute( - value: string, options: PropertyDeclaration) { + value: string|null, options: PropertyDeclaration) { const type = options.type; const converter = options.converter || defaultConverter; const fromAttribute = @@ -468,6 +478,7 @@ export abstract class UpdatingElement extends HTMLElement { private _applyInstanceProperties() { // Use forEach so this works even if for/of loops are compiled to for loops // expecting arrays + // tslint:disable-next-line:no-any this._instanceProperties!.forEach((v, p) => (this as any)[p] = v); this._instanceProperties = undefined; } @@ -497,7 +508,7 @@ export abstract class UpdatingElement extends HTMLElement { /** * Synchronizes property values when attributes change. */ - attributeChangedCallback(name: string, old: string, value: string) { + attributeChangedCallback(name: string, old: string|null, value: string|null) { if (old !== value) { this._attributeToProperty(name, value); } @@ -526,14 +537,14 @@ export abstract class UpdatingElement extends HTMLElement { if (attrValue == null) { this.removeAttribute(attr); } else { - this.setAttribute(attr, attrValue); + this.setAttribute(attr, attrValue as string); } // mark state not reflecting this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE; } } - private _attributeToProperty(name: string, value: string) { + private _attributeToProperty(name: string, value: string|null) { // Use tracking info to avoid deserializing attribute value if it was // just set from a property setter. if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) { @@ -547,7 +558,8 @@ export abstract class UpdatingElement extends HTMLElement { // mark state reflecting this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY; this[propName as keyof this] = - ctor._propertyValueFromAttribute(value, options); + // tslint:disable-next-line:no-any + ctor._propertyValueFromAttribute(value, options) as any; // mark state not reflecting this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY; } @@ -566,7 +578,7 @@ export abstract class UpdatingElement extends HTMLElement { * @param oldValue {any} (optional) old value of requesting property * @returns {Promise} A Promise that is resolved when the update completes. */ - requestUpdate(name?: PropertyKey, oldValue?: any) { + requestUpdate(name?: PropertyKey, oldValue?: unknown) { let shouldRequestUpdate = true; // if we have a property key, perform property update steps. if (name !== undefined && !this._changedProperties.has(name)) { diff --git a/src/lit-element.ts b/src/lit-element.ts index 558ec603..e2bbada9 100644 --- a/src/lit-element.ts +++ b/src/lit-element.ts @@ -208,7 +208,7 @@ export class LitElement extends UpdatingElement { */ protected update(changedProperties: PropertyValues) { super.update(changedProperties); - const templateResult = this.render() as any; + const templateResult = this.render() as unknown; if (templateResult instanceof TemplateResult) { (this.constructor as typeof LitElement) .render( diff --git a/src/test/lib/decorators_test.ts b/src/test/lib/decorators_test.ts index c63b7a2f..9ecbfcff 100644 --- a/src/test/lib/decorators_test.ts +++ b/src/test/lib/decorators_test.ts @@ -16,6 +16,8 @@ import {eventOptions, property} from '../../lib/decorators.js'; import {customElement, html, LitElement, PropertyValues, query, queryAll} from '../../lit-element.js'; import {generateElementName} from '../test-helpers.js'; +// tslint:disable:no-any ok in tests + let hasOptions; const supportsOptions = (function() { if (hasOptions !== undefined) { diff --git a/src/test/lib/updating-element_test.ts b/src/test/lib/updating-element_test.ts index ebcea360..8daa277f 100644 --- a/src/test/lib/updating-element_test.ts +++ b/src/test/lib/updating-element_test.ts @@ -16,6 +16,8 @@ import {property} from '../../lib/decorators.js'; import {ComplexAttributeConverter, PropertyDeclarations, PropertyValues, UpdatingElement} from '../../lib/updating-element.js'; import {generateElementName} from '../test-helpers.js'; +// tslint:disable:no-any ok in tests + const assert = chai.assert; suite('UpdatingElement', () => { diff --git a/src/test/lit-element_styling_test.ts b/src/test/lit-element_styling_test.ts index 1a34a58d..819db6e4 100644 --- a/src/test/lit-element_styling_test.ts +++ b/src/test/lit-element_styling_test.ts @@ -456,6 +456,7 @@ suite('Static get styles', () => { test('`css` get styles throws when unsafe values are used', async () => { assert.throws(() => { + // tslint:disable:no-any intentionally unsafe code css`div { border: ${`2px solid blue;` as any}}`; }); }); diff --git a/src/test/lit-element_test.ts b/src/test/lit-element_test.ts index 89b9dc2e..72c18b5c 100644 --- a/src/test/lit-element_test.ts +++ b/src/test/lit-element_test.ts @@ -18,6 +18,8 @@ import {generateElementName, stripExpressionDelimeters} from './test-helpers.js' const assert = chai.assert; +// tslint:disable:no-any ok in tests + suite('LitElement', () => { let container: HTMLElement; diff --git a/tsconfig.json b/tsconfig.json index 1aa2cc75..b904df43 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,8 @@ "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitThis": true, "outDir": "./", // Only necessary because @types/uglify-js can't find types for source-map "skipLibCheck": true, diff --git a/tslint.json b/tslint.json index 54984fb6..7b4fc2a9 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,7 @@ "spaces" ], "prefer-const": true, + "no-any": true, "no-duplicate-variable": true, "no-eval": true, "no-internal-module": true,