Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {LitElement} from '../lit-element.js';
import {PropertyDeclaration, UpdatingElement} from './updating-element.js';

export type Constructor<T> = {
new (...args: any[]): T
new (...args: unknown[]): T
};

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

Expand Down Expand Up @@ -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);
Expand All @@ -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) :
Expand Down Expand Up @@ -177,6 +180,7 @@ const standardQuery = (descriptor: PropertyDescriptor, element: ClassElement) =>
function _query<T>(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) {
Expand All @@ -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);
};
Expand Down Expand Up @@ -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;
58 changes: 35 additions & 23 deletions src/lib/updating-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,42 +33,45 @@ declare global {
/**
* Converts property values to and from attribute values.
*/
export interface ComplexAttributeConverter<Type = any, TypeHint = any> {
export interface ComplexAttributeConverter<Type = unknown, TypeHint = unknown> {
/**
* 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 = any, TypeHint = any> =
type AttributeConverter<Type = unknown, TypeHint = unknown> =
ComplexAttributeConverter<Type>|((value: string, type?: TypeHint) => Type);

/**
* Defines options for a property accessor.
*/
export interface PropertyDeclaration<Type = any, TypeHint = any> {
export interface PropertyDeclaration<Type = unknown, TypeHint = unknown> {
/**
* Indicates how and whether the property becomes an observed attribute.
* If the value is `false`, the property is not added to `observedAttributes`.
* If true or absent, the lowercased property name is observed (e.g. `fooBar`
* 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
Expand All @@ -82,7 +85,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
* the property is never updated again as a result of the attribute changing,
* and vice versa.
*/
converter?: AttributeConverter<Type, TypeHint>;
readonly converter?: AttributeConverter<Type, TypeHint>;

/**
* Indicates if the property should reflect to an attribute.
Expand All @@ -91,7 +94,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
* 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
Expand All @@ -108,7 +111,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
* `this.requestUpdate(propertyName, oldValue)` to request an update when
* the property changes.
*/
noAccessor?: boolean;
readonly noAccessor?: boolean;
}

/**
Expand All @@ -117,7 +120,7 @@ export interface PropertyDeclaration<Type = any, TypeHint = any> {
* PropertyDeclaration options.
*/
export interface PropertyDeclarations {
[key: string]: PropertyDeclaration;
readonly [key: string]: PropertyDeclaration;
}

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

export const defaultConverter: ComplexAttributeConverter = {

toAttribute(value: any, type?: any) {
toAttribute(value: unknown, type?: unknown): unknown {
switch (type) {
case Boolean:
return value ? '' : null;
Expand All @@ -141,15 +144,15 @@ export const defaultConverter: ComplexAttributeConverter = {
return value;
},

fromAttribute(value: any, type?: any) {
fromAttribute(value: string|null, type?: unknown) {
switch (type) {
case Boolean:
return value !== null;
case Number:
return value === null ? null : Number(value);
case Object:
case Array:
return JSON.parse(value);
return JSON.parse(value!);
}
return value;
}
Expand Down Expand Up @@ -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));
}
}
}
Expand Down Expand Up @@ -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);
},
Expand Down Expand Up @@ -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]);
}
}
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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)) {
Expand Down
2 changes: 1 addition & 1 deletion src/lit-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions src/test/lib/decorators_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions src/test/lib/updating-element_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
1 change: 1 addition & 0 deletions src/test/lit-element_styling_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}}`;
});
});
Expand Down
2 changes: 2 additions & 0 deletions src/test/lit-element_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"spaces"
],
"prefer-const": true,
"no-any": true,
"no-duplicate-variable": true,
"no-eval": true,
"no-internal-module": true,
Expand Down