diff --git a/.flowconfig b/.flowconfig index 0aaae7a8923..3cc1d154e63 100644 --- a/.flowconfig +++ b/.flowconfig @@ -22,3 +22,10 @@ [options] server.max_workers=4 + +[strict] +nonstrict-import +unclear-type +untyped-import +untyped-type-import +sketchy-null diff --git a/flow-typed/gl.js b/flow-typed/gl.js new file mode 100644 index 00000000000..7fe9f46faab --- /dev/null +++ b/flow-typed/gl.js @@ -0,0 +1,5 @@ +// @flow strict +declare module "gl" { + declare function gl(width: number, height: number, attributes: WebGLContextAttributes): WebGLRenderingContext; + declare module.exports: typeof gl; +} diff --git a/flow-typed/jsdom.js b/flow-typed/jsdom.js new file mode 100644 index 00000000000..d9aa8c4080b --- /dev/null +++ b/flow-typed/jsdom.js @@ -0,0 +1,18 @@ +// @flow strict + +import type Window from '../src/types/window'; + +declare module "jsdom" { + declare class JSDOM { + constructor(content: string, options: Object): JSDOM; + window: Window; + } + declare class VirtualConsole { + constructor(): VirtualConsole; + sendTo(console: typeof console): VirtualConsole; + } + declare module.exports: { + JSDOM: typeof JSDOM, + VirtualConsole: typeof VirtualConsole + }; +} diff --git a/flow-typed/sinon.js b/flow-typed/sinon.js new file mode 100644 index 00000000000..325bd11f32d --- /dev/null +++ b/flow-typed/sinon.js @@ -0,0 +1,10 @@ +// @flow strict +declare module "sinon" { + declare class FakeServer { + xhr: XMLHttpRequest + } + declare module.exports: { + xhr: {supportsCORS: boolean}, + fakeServer: {create: () => FakeServer} + }; +} diff --git a/flow-typed/unitbezier.js b/flow-typed/unitbezier.js deleted file mode 100644 index 6a6c29a84fe..00000000000 --- a/flow-typed/unitbezier.js +++ /dev/null @@ -1,11 +0,0 @@ -declare module "unitbezier" { - declare class UnitBezier { - constructor(p1x: number, p1y: number, p2x: number, p2y: number): UnitBezier; - sampleCurveX(t: number): number; - sampleCurveY(t: number): number; - sampleCurveDerivativeX(t: number): number; - solveCurveX(t: number): number; - solve(x: number, epsilon?: number): number; - } - declare module.exports: typeof UnitBezier; -} diff --git a/src/types/callback.js b/src/types/callback.js index 1c5416c00fc..217eff73014 100644 --- a/src/types/callback.js +++ b/src/types/callback.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict // Flow can't perfectly type Node-style callbacks yet; it does not have a way to // express that if the first parameter is null, the second is not, so for the time diff --git a/src/types/cancelable.js b/src/types/cancelable.js index 08942dafa02..e155168ac00 100644 --- a/src/types/cancelable.js +++ b/src/types/cancelable.js @@ -1,3 +1,3 @@ -// @flow +// @flow strict export type Cancelable = { cancel: () => void }; diff --git a/src/types/window.js b/src/types/window.js index 54bf7d2f8c5..27ed038086c 100644 --- a/src/types/window.js +++ b/src/types/window.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict /* global EventTarget, IDBEnvironment */ @@ -22,83 +22,83 @@ export interface Window extends EventTarget, IDBEnvironment { name: string; +navigator: Navigator; offscreenBuffering: string | boolean; - onabort: (ev: UIEvent) => any; - onafterprint: (ev: Event) => any; - onbeforeprint: (ev: Event) => any; - onbeforeunload: (ev: Event) => any; - onblur: (ev: FocusEvent) => any; - oncanplay: (ev: Event) => any; - oncanplaythrough: (ev: Event) => any; - onchange: (ev: Event) => any; - onclick: (ev: MouseEvent) => any; - oncompassneedscalibration: (ev: Event) => any; - oncontextmenu: (ev: Event) => any; - ondblclick: (ev: MouseEvent) => any; - ondevicelight: (ev: Event) => any; - ondevicemotion: (ev: Event) => any; - ondeviceorientation: (ev: Event) => any; - ondrag: (ev: DragEvent) => any; - ondragend: (ev: DragEvent) => any; - ondragenter: (ev: DragEvent) => any; - ondragleave: (ev: DragEvent) => any; - ondragover: (ev: DragEvent) => any; - ondragstart: (ev: DragEvent) => any; - ondrop: (ev: DragEvent) => any; - ondurationchange: (ev: Event) => any; - onemptied: (ev: Event) => any; - onended: (ev: Event) => any; - onerror: (ev: Event) => any; - onfocus: (ev: FocusEvent) => any; - onhashchange: (ev: Event) => any; - oninput: (ev: Event) => any; - oninvalid: (ev: Event) => any; - onkeydown: (ev: KeyboardEvent) => any; - onkeypress: (ev: KeyboardEvent) => any; - onkeyup: (ev: KeyboardEvent) => any; - onload: (ev: Event) => any; - onloadeddata: (ev: Event) => any; - onloadedmetadata: (ev: Event) => any; - onloadstart: (ev: Event) => any; - onmessage: (ev: MessageEvent) => any; - onmousedown: (ev: MouseEvent) => any; - onmouseenter: (ev: MouseEvent) => any; - onmouseleave: (ev: MouseEvent) => any; - onmousemove: (ev: MouseEvent) => any; - onmouseout: (ev: MouseEvent) => any; - onmouseover: (ev: MouseEvent) => any; - onmouseup: (ev: MouseEvent) => any; - onmousewheel: (ev: WheelEvent) => any; - onoffline: (ev: Event) => any; - ononline: (ev: Event) => any; - onorientationchange: (ev: Event) => any; - onpagehide: (ev: Event) => any; - onpageshow: (ev: Event) => any; - onpause: (ev: Event) => any; - onplay: (ev: Event) => any; - onplaying: (ev: Event) => any; - onpopstate: (ev: Event) => any; - onprogress: (ev: ProgressEvent) => any; - onratechange: (ev: Event) => any; - onreadystatechange: (ev: ProgressEvent) => any; - onreset: (ev: Event) => any; - onresize: (ev: UIEvent) => any; - onscroll: (ev: UIEvent) => any; - onseeked: (ev: Event) => any; - onseeking: (ev: Event) => any; - onselect: (ev: UIEvent) => any; - onstalled: (ev: Event) => any; - onstorage: (ev: Event) => any; - onsubmit: (ev: Event) => any; - onsuspend: (ev: Event) => any; - ontimeupdate: (ev: Event) => any; - ontouchcancel: (ev: TouchEvent) => any; - ontouchend: (ev: TouchEvent) => any; - ontouchmove: (ev: TouchEvent) => any; - ontouchstart: (ev: TouchEvent) => any; - onunload: (ev: Event) => any; - onvolumechange: (ev: Event) => any; - onwaiting: (ev: Event) => any; - opener: any; + onabort: (ev: UIEvent) => ?boolean; + onafterprint: (ev: Event) => ?boolean; + onbeforeprint: (ev: Event) => ?boolean; + onbeforeunload: (ev: Event) => ?boolean; + onblur: (ev: FocusEvent) => ?boolean; + oncanplay: (ev: Event) => ?boolean; + oncanplaythrough: (ev: Event) => ?boolean; + onchange: (ev: Event) => ?boolean; + onclick: (ev: MouseEvent) => ?boolean; + oncompassneedscalibration: (ev: Event) => ?boolean; + oncontextmenu: (ev: Event) => ?boolean; + ondblclick: (ev: MouseEvent) => ?boolean; + ondevicelight: (ev: Event) => ?boolean; + ondevicemotion: (ev: Event) => ?boolean; + ondeviceorientation: (ev: Event) => ?boolean; + ondrag: (ev: DragEvent) => ?boolean; + ondragend: (ev: DragEvent) => ?boolean; + ondragenter: (ev: DragEvent) => ?boolean; + ondragleave: (ev: DragEvent) => ?boolean; + ondragover: (ev: DragEvent) => ?boolean; + ondragstart: (ev: DragEvent) => ?boolean; + ondrop: (ev: DragEvent) => ?boolean; + ondurationchange: (ev: Event) => ?boolean; + onemptied: (ev: Event) => ?boolean; + onended: (ev: Event) => ?boolean; + onerror: (ev: Event) => ?boolean; + onfocus: (ev: FocusEvent) => ?boolean; + onhashchange: (ev: Event) => ?boolean; + oninput: (ev: Event) => ?boolean; + oninvalid: (ev: Event) => ?boolean; + onkeydown: (ev: KeyboardEvent) => ?boolean; + onkeypress: (ev: KeyboardEvent) => ?boolean; + onkeyup: (ev: KeyboardEvent) => ?boolean; + onload: (ev: Event) => ?boolean; + onloadeddata: (ev: Event) => ?boolean; + onloadedmetadata: (ev: Event) => ?boolean; + onloadstart: (ev: Event) => ?boolean; + onmessage: (ev: MessageEvent) => ?boolean; + onmousedown: (ev: MouseEvent) => ?boolean; + onmouseenter: (ev: MouseEvent) => ?boolean; + onmouseleave: (ev: MouseEvent) => ?boolean; + onmousemove: (ev: MouseEvent) => ?boolean; + onmouseout: (ev: MouseEvent) => ?boolean; + onmouseover: (ev: MouseEvent) => ?boolean; + onmouseup: (ev: MouseEvent) => ?boolean; + onmousewheel: (ev: WheelEvent) => ?boolean; + onoffline: (ev: Event) => ?boolean; + ononline: (ev: Event) => ?boolean; + onorientationchange: (ev: Event) => ?boolean; + onpagehide: (ev: Event) => ?boolean; + onpageshow: (ev: Event) => ?boolean; + onpause: (ev: Event) => ?boolean; + onplay: (ev: Event) => ?boolean; + onplaying: (ev: Event) => ?boolean; + onpopstate: (ev: Event) => ?boolean; + onprogress: (ev: ProgressEvent) => ?boolean; + onratechange: (ev: Event) => ?boolean; + onreadystatechange: (ev: ProgressEvent) => ?boolean; + onreset: (ev: Event) => ?boolean; + onresize: (ev: UIEvent) => ?boolean; + onscroll: (ev: UIEvent) => ?boolean; + onseeked: (ev: Event) => ?boolean; + onseeking: (ev: Event) => ?boolean; + onselect: (ev: UIEvent) => ?boolean; + onstalled: (ev: Event) => ?boolean; + onstorage: (ev: Event) => ?boolean; + onsubmit: (ev: Event) => ?boolean; + onsuspend: (ev: Event) => ?boolean; + ontimeupdate: (ev: Event) => ?boolean; + ontouchcancel: (ev: TouchEvent) => ?boolean; + ontouchend: (ev: TouchEvent) => ?boolean; + ontouchmove: (ev: TouchEvent) => ?boolean; + ontouchstart: (ev: TouchEvent) => ?boolean; + onunload: (ev: Event) => ?boolean; + onvolumechange: (ev: Event) => ?boolean; + onwaiting: (ev: Event) => ?boolean; + opener: Window; orientation: string | number; +outerHeight: number; +outerWidth: number; @@ -133,9 +133,9 @@ export interface Window extends EventTarget, IDBEnvironment { Worker: typeof Worker; XMLHttpRequest: typeof XMLHttpRequest; Request: typeof Request; - AbortController: any; + AbortController: typeof AbortController; - alert(message?: any): void; + alert(message?: string): void; blur(): void; captureEvents(): void; close(): void; @@ -148,7 +148,7 @@ export interface Window extends EventTarget, IDBEnvironment { moveTo(x?: number, y?: number): void; msWriteProfilerMark(profilerMarkName: string): void; open(url?: string, target?: string, features?: string, replace?: boolean): Window; - postMessage(message: any, targetOrigin: string, transfer?: any[]): void; + postMessage(message: mixed, targetOrigin: string, transfer?: ArrayBuffer[]): void; print(): void; prompt(message?: string, _default?: string): string | null; releaseEvents(): void; diff --git a/src/ui/hash.js b/src/ui/hash.js index 057addbd265..938accae4fa 100644 --- a/src/ui/hash.js +++ b/src/ui/hash.js @@ -14,7 +14,7 @@ import type Map from './map'; */ class Hash { _map: Map; - _updateHash: () => TimeoutID; + _updateHash: () => ?TimeoutID; constructor() { bindAll([ diff --git a/src/util/browser.js b/src/util/browser.js index ec18d8f6fba..004609d877f 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict import window from './window'; import type { Cancelable } from '../types/cancelable'; @@ -29,7 +29,7 @@ const exported = { */ now, - frame(fn: Function): Cancelable { + frame(fn: () => void): Cancelable { const frame = raf(fn); return { cancel: () => cancel(frame) }; }, diff --git a/src/util/config.js b/src/util/config.js index 58a55f04359..8c9872d1d20 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict type Config = {| API_URL: string, diff --git a/src/util/dictionary_coder.js b/src/util/dictionary_coder.js index 1b8302f8e29..9dc914f43b2 100644 --- a/src/util/dictionary_coder.js +++ b/src/util/dictionary_coder.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict import assert from 'assert'; diff --git a/src/util/dom.js b/src/util/dom.js index 7281996184d..c015c87a14b 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict import Point from '@mapbox/point-geometry'; @@ -8,9 +8,9 @@ import assert from 'assert'; const DOM = {}; export default DOM; -DOM.create = function (tagName: *, className?: string, container?: HTMLElement) { +DOM.create = function (tagName: string, className: ?string, container?: HTMLElement) { const el = window.document.createElement(tagName); - if (className) el.className = className; + if (className !== undefined) el.className = className; if (container) container.appendChild(el); return el; }; @@ -20,12 +20,10 @@ DOM.createNS = function (namespaceURI: string, tagName: string) { return el; }; -const docStyle = window.document ? - (window.document.documentElement: any).style : - null; +const docStyle = window.document.documentElement.style; function testProp(props) { - if (!docStyle) return null; + if (!docStyle) return props[0]; for (let i = 0; i < props.length; i++) { if (props[i] in docStyle) { return props[i]; @@ -53,27 +51,31 @@ DOM.enableDrag = function () { const transformProp = testProp(['transform', 'WebkitTransform']); DOM.setTransform = function(el: HTMLElement, value: string) { - (el.style: any)[transformProp] = value; + // https://github.com/facebook/flow/issues/7754 + // $FlowFixMe + el.style[transformProp] = value; }; // Feature detection for {passive: false} support in add/removeEventListener. let passiveSupported = false; try { - const options = (Object.defineProperty: any)({}, "passive", { + const options = Object.defineProperty({}, "passive", { get() { passiveSupported = true; - } + return false; + }, + value: false }); - (window.addEventListener: any)("test", options, options); - (window.removeEventListener: any)("test", options, options); + window.addEventListener("test", options, options); + window.removeEventListener("test", options, options); } catch (err) { passiveSupported = false; } DOM.addEventListener = function(target: *, type: *, callback: *, options: {passive?: boolean, capture?: boolean} = {}) { if ('passive' in options && passiveSupported) { - target.addEventListener(type, callback, (options: any)); + target.addEventListener(type, callback, options); } else { target.addEventListener(type, callback, options.capture); } @@ -81,7 +83,7 @@ DOM.addEventListener = function(target: *, type: *, callback: *, options: {passi DOM.removeEventListener = function(target: *, type: *, callback: *, options: {passive?: boolean, capture?: boolean} = {}) { if ('passive' in options && passiveSupported) { - target.removeEventListener(type, callback, (options: any)); + target.removeEventListener(type, callback, options); } else { target.removeEventListener(type, callback, options.capture); } @@ -101,16 +103,16 @@ DOM.suppressClick = function() { }, 0); }; -DOM.mousePos = function (el: HTMLElement, e: any) { +DOM.mousePos = function (el: HTMLElement, e: MouseEvent | window.TouchEvent | Touch) { const rect = el.getBoundingClientRect(); - e = e.touches ? e.touches[0] : e; + const t = window.TouchEvent && (e instanceof window.TouchEvent) ? e.touches[0] : e; return new Point( - e.clientX - rect.left - el.clientLeft, - e.clientY - rect.top - el.clientTop + t.clientX - rect.left - el.clientLeft, + t.clientY - rect.top - el.clientTop ); }; -DOM.touchPos = function (el: HTMLElement, e: any) { +DOM.touchPos = function (el: HTMLElement, e: TouchEvent) { const rect = el.getBoundingClientRect(), points = []; const touches = (e.type === 'touchend') ? e.changedTouches : e.touches; diff --git a/src/util/task_queue.js b/src/util/task_queue.js index d3059eb8982..a45a3deb6c1 100644 --- a/src/util/task_queue.js +++ b/src/util/task_queue.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict import assert from 'assert'; export type TaskID = number; // can't mark opaque due to https://github.com/flowtype/flow-remove-types/pull/61 diff --git a/src/util/throttle.js b/src/util/throttle.js index a525d85f006..2ab3baed7b3 100644 --- a/src/util/throttle.js +++ b/src/util/throttle.js @@ -1,16 +1,16 @@ -// @flow +// @flow strict /** * Throttle the given function to run at most every `period` milliseconds. Throttle the given function to run at most every period milliseconds. * @private */ -export default function throttle(fn: () => void, time: number): () => TimeoutID { +export default function throttle(fn: () => void, time: number): () => ?TimeoutID { let pending = false; - let timerId: TimeoutID = (0: any); + let timerId: ?TimeoutID = null; const later = () => { - timerId = (0: any); + timerId = null; if (pending) { fn(); timerId = setTimeout(later, time); diff --git a/src/util/webp_supported.js b/src/util/webp_supported.js index e787d00d5e4..7c40c7c97b5 100755 --- a/src/util/webp_supported.js +++ b/src/util/webp_supported.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict import window from './window'; diff --git a/src/util/window.js b/src/util/window.js index f053734e6ac..6957f438a60 100644 --- a/src/util/window.js +++ b/src/util/window.js @@ -1,4 +1,4 @@ -// @flow +// @flow strict // This file is intended for use in the GL-JS test suite // It implements a JSDOM window object for use in Node environments @@ -10,7 +10,6 @@ import jsdom from 'jsdom'; import gl from 'gl'; import sinon from 'sinon'; -import { extend } from './util'; import type {Window} from '../types/window'; @@ -28,7 +27,7 @@ function restore(): Window { if (previousWindow.close) previousWindow.close(); for (const key in previousWindow) { if (previousWindow.hasOwnProperty(key)) { - delete (previousWindow: any)[key]; + delete previousWindow[key]; } } @@ -82,7 +81,7 @@ function restore(): Window { window.ImageData = window.ImageData || function() { return false; }; window.ImageBitmap = window.ImageBitmap || function() { return false; }; window.WebGLFramebuffer = window.WebGLFramebuffer || Object; - extend(_window, window); + Object.assign(_window, window); // eslint-disable-line no-restricted-properties return window; }