Fix is.domElement to more accurately reflect that the object is an HTMLElement

This commit is contained in:
Bjorn Stromberg 2020-06-07 14:41:07 +09:00
parent e5eb98e4aa
commit 30572a045d
2 changed files with 28 additions and 19 deletions

View file

@ -60,6 +60,7 @@ const objectTypeNames = [
'DataView', 'DataView',
'Promise', 'Promise',
'URL', 'URL',
'HTMLElement',
...typedArrayTypeNames ...typedArrayTypeNames
] as const; ] as const;
@ -102,6 +103,10 @@ const isOfType = <T>(type: string) => (value: unknown): value is T => typeof val
const getObjectType = (value: unknown): ObjectTypeName | undefined => { const getObjectType = (value: unknown): ObjectTypeName | undefined => {
const objectTypeName = toString.call(value).slice(8, -1); const objectTypeName = toString.call(value).slice(8, -1);
if (/HTML\w+Element/.test(objectTypeName) && is.domElement(value)) {
return 'HTMLElement';
}
if (isObjectTypeName(objectTypeName)) { if (isObjectTypeName(objectTypeName)) {
return objectTypeName; return objectTypeName;
} }
@ -268,7 +273,7 @@ is.safeInteger = (value: unknown): value is number => Number.isSafeInteger(value
is.plainObject = <Value = unknown>(value: unknown): value is Record<string, Value> => { is.plainObject = <Value = unknown>(value: unknown): value is Record<string, Value> => {
// From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js // From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js
if (getObjectType(value) !== 'Object') { if (toString.call(value) !== '[object Object]') {
return false; return false;
} }
@ -300,7 +305,7 @@ is.inRange = (value: number, range: number | number[]): value is number => {
}; };
const NODE_TYPE_ELEMENT = 1; const NODE_TYPE_ELEMENT = 1;
const DOM_PROPERTIES_TO_CHECK = [ const DOM_PROPERTIES_TO_CHECK: Array<(keyof HTMLElement)> = [
'innerHTML', 'innerHTML',
'ownerDocument', 'ownerDocument',
'style', 'style',
@ -308,8 +313,13 @@ const DOM_PROPERTIES_TO_CHECK = [
'nodeValue' 'nodeValue'
]; ];
is.domElement = (value: unknown): value is Element => is.object(value) && (value as Element).nodeType === NODE_TYPE_ELEMENT && is.string((value as Element).nodeName) && is.domElement = (value: unknown): value is HTMLElement => {
!is.plainObject(value) && DOM_PROPERTIES_TO_CHECK.every(property => property in (value as Element)); return is.object(value) &&
(value as HTMLElement).nodeType === NODE_TYPE_ELEMENT &&
is.string((value as HTMLElement).nodeName) &&
!is.plainObject(value) &&
DOM_PROPERTIES_TO_CHECK.every(property => property in value);
};
export interface ObservableLike { export interface ObservableLike {
subscribe(observer: (value: unknown) => void): void; subscribe(observer: (value: unknown) => void): void;
@ -416,7 +426,7 @@ export const enum AssertionTypeDescription {
plainObject = 'plain object', plainObject = 'plain object',
arrayLike = 'array-like', arrayLike = 'array-like',
typedArray = 'TypedArray', typedArray = 'TypedArray',
domElement = 'Element', domElement = 'HTMLElement',
nodeStream = 'Node.js Stream', nodeStream = 'Node.js Stream',
infinite = 'infinite number', infinite = 'infinite number',
emptyArray = 'empty array', emptyArray = 'empty array',
@ -503,7 +513,7 @@ interface Assert {
plainObject: <Value = unknown>(value: unknown) => asserts value is Record<string, Value>; plainObject: <Value = unknown>(value: unknown) => asserts value is Record<string, Value>;
typedArray: (value: unknown) => asserts value is TypedArray; typedArray: (value: unknown) => asserts value is TypedArray;
arrayLike: <T = unknown>(value: unknown) => asserts value is ArrayLike<T>; arrayLike: <T = unknown>(value: unknown) => asserts value is ArrayLike<T>;
domElement: (value: unknown) => asserts value is Element; domElement: (value: unknown) => asserts value is HTMLElement;
observable: (value: unknown) => asserts value is ObservableLike; observable: (value: unknown) => asserts value is ObservableLike;
nodeStream: (value: unknown) => asserts value is NodeStream; nodeStream: (value: unknown) => asserts value is NodeStream;
infinite: (value: unknown) => asserts value is number; infinite: (value: unknown) => asserts value is number;
@ -593,7 +603,7 @@ export const assert: Assert = {
plainObject: <Value = unknown>(value: unknown): asserts value is Record<string, Value> => assertType(is.plainObject(value), AssertionTypeDescription.plainObject, value), plainObject: <Value = unknown>(value: unknown): asserts value is Record<string, Value> => assertType(is.plainObject(value), AssertionTypeDescription.plainObject, value),
typedArray: (value: unknown): asserts value is TypedArray => assertType(is.typedArray(value), AssertionTypeDescription.typedArray, value), typedArray: (value: unknown): asserts value is TypedArray => assertType(is.typedArray(value), AssertionTypeDescription.typedArray, value),
arrayLike: <T = unknown>(value: unknown): asserts value is ArrayLike<T> => assertType(is.arrayLike(value), AssertionTypeDescription.arrayLike, value), arrayLike: <T = unknown>(value: unknown): asserts value is ArrayLike<T> => assertType(is.arrayLike(value), AssertionTypeDescription.arrayLike, value),
domElement: (value: unknown): asserts value is Element => assertType(is.domElement(value), AssertionTypeDescription.domElement, value), domElement: (value: unknown): asserts value is HTMLElement => assertType(is.domElement(value), AssertionTypeDescription.domElement, value),
observable: (value: unknown): asserts value is ObservableLike => assertType(is.observable(value), 'Observable', value), observable: (value: unknown): asserts value is ObservableLike => assertType(is.observable(value), 'Observable', value),
nodeStream: (value: unknown): asserts value is NodeStream => assertType(is.nodeStream(value), AssertionTypeDescription.nodeStream, value), nodeStream: (value: unknown): asserts value is NodeStream => assertType(is.nodeStream(value), AssertionTypeDescription.nodeStream, value),
infinite: (value: unknown): asserts value is number => assertType(is.infinite(value), AssertionTypeDescription.infinite, value), infinite: (value: unknown): asserts value is number => assertType(is.infinite(value), AssertionTypeDescription.infinite, value),

View file

@ -591,7 +591,7 @@ const testType = (t: ExecutionContext, type: string, exclude?: string[]) => {
} }
if (isTypeUnderTest && typename) { if (isTypeUnderTest && typename) {
t.is(is(fixture), typename); t.is<TypeName>(is(fixture), typename);
} }
} }
} }
@ -1275,18 +1275,17 @@ test('is.domElement', t => {
assert.domElement({nodeType: 1, nodeName: 'div'}); assert.domElement({nodeType: 1, nodeName: 'div'});
}); });
const htmlTagNameToTypeName = { const tagNames = [
div: 'HTMLDivElement', 'div',
input: 'HTMLInputElement', 'input',
span: 'HTMLSpanElement', 'span',
img: 'HTMLImageElement', 'img',
canvas: 'HTMLCanvasElement', 'canvas',
script: 'HTMLScriptElement' 'script'
}; ];
for (const [tagName, typeName] of Object.entries(htmlTagNameToTypeName)) { for (const tagName of tagNames) {
const domElement = createDomElement(tagName); t.is<TypeName>(is(createDomElement(tagName)), 'HTMLElement');
t.is(is(domElement), typeName);
} }
}); });