From f9a4f741a9f446cdbdd22eea867819d958f182c0 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 17 Aug 2021 08:51:17 -0400 Subject: [PATCH] strict type checks --- CHANGELOG.md | 2 +- TODO.md | 14 ++++----- demo/index-pwa.js | 2 -- demo/index-worker.js | 5 ++- demo/multithread/worker.js | 3 +- package.json | 4 +-- src/age/age.ts | 5 ++- src/draw/draw.ts | 3 -- src/efficientpose/efficientpose.ts | 3 +- src/face.ts | 2 +- src/faceres/faceres.ts | 3 +- src/gender/gender.ts | 3 +- src/handpose/handdetector.ts | 3 +- src/handpose/handpose.ts | 5 ++- src/helpers.ts | 2 +- src/human.ts | 50 ++++++++++-------------------- src/image/image.ts | 2 +- src/models.ts | 12 ------- src/movenet/movenet.ts | 3 +- src/persons.ts | 4 +-- src/posenet/utils.ts | 4 +-- tsconfig.json | 38 ++++++++++++++--------- 22 files changed, 70 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11eeb97a..ab4dbdfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Repository: **** ## Changelog -### **HEAD -> main** 2021/08/14 mandic00@live.com +### **HEAD -> main** 2021/08/15 mandic00@live.com - experimental webgpu support - add experimental webgu demo diff --git a/TODO.md b/TODO.md index 75b9a214..47eb58a0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,12 +1,5 @@ # To-Do list for Human library -## Big Ticket Items - -Implementation of WebGPU backend -*Target: `Human` v2.3 with `Chrome` v94 and `TFJS` v4.0* - -
- ## Work in Progress WebGL shader optimizations for faster load and initial detection @@ -69,10 +62,15 @@ Object detection using CenterNet or NanoDet models is not working when using WAS *Target: `Human` v2.2 with `TFJS` v3.9* -### WebGPU +### WebGPU Backend +Implementation of WebGPU backend Experimental support only +*Target: `Human` v2.3 with `Chrome` v94 and `TFJS` v4.0* + +
+ - Backend WebGPU missing kernel ops - Backend WebGPU incompatible with web workers diff --git a/demo/index-pwa.js b/demo/index-pwa.js index a8961083..6f20f58d 100644 --- a/demo/index-pwa.js +++ b/demo/index-pwa.js @@ -4,8 +4,6 @@ /// -// // @ts-nocheck Linting of ServiceWorker is not supported for JS files - const skipCaching = false; const cacheName = 'Human'; diff --git a/demo/index-worker.js b/demo/index-worker.js index 92bb1492..1c8afaf3 100644 --- a/demo/index-worker.js +++ b/demo/index-worker.js @@ -1,3 +1,5 @@ +/// + // load Human using IIFE script as Chome Mobile does not support Modules as Workers // import Human from '../dist/human.esm.js'; self.importScripts('../dist/human.js'); @@ -34,12 +36,9 @@ onmessage = async (msg) => { if (ctx) ctx.drawImage(result.canvas, 0, 0); const img = ctx ? ctx.getImageData(0, 0, result.canvas.width, result.canvas.height) : null; result.canvas = null; // must strip original canvas from return value as it cannot be transfered from worker thread - // @ts-ignore tslint wrong type matching for worker if (img) postMessage({ result, image: img.data.buffer, width: msg.data.width, height: msg.data.height }, [img.data.buffer]); - // @ts-ignore tslint wrong type matching for worker else postMessage({ result }); } else { - // @ts-ignore tslint wrong type matching for worker postMessage({ result }); } busy = false; diff --git a/demo/multithread/worker.js b/demo/multithread/worker.js index bfbf3fc5..3314ae64 100644 --- a/demo/multithread/worker.js +++ b/demo/multithread/worker.js @@ -1,5 +1,7 @@ // load Human using IIFE script as Chome Mobile does not support Modules as Workers +/// + // import Human from '../dist/human.esm.js'; self.importScripts('../../dist/human.js'); @@ -15,6 +17,5 @@ onmessage = async (msg) => { const image = new ImageData(new Uint8ClampedArray(msg.data.image), msg.data.width, msg.data.height); let result = {}; result = await human.detect(image, msg.data.config); - // @ts-ignore tslint wrong type matching for worker postMessage({ result: result[msg.data.type], type: msg.data.type }); }; diff --git a/package.json b/package.json index d678b877..3ba63da6 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,8 @@ "@tensorflow/tfjs-node": "^3.8.0", "@tensorflow/tfjs-node-gpu": "^3.8.0", "@types/node": "^16.6.1", - "@typescript-eslint/eslint-plugin": "^4.29.1", - "@typescript-eslint/parser": "^4.29.1", + "@typescript-eslint/eslint-plugin": "^4.29.2", + "@typescript-eslint/parser": "^4.29.2", "@vladmandic/pilogger": "^0.2.18", "canvas": "^2.8.0", "chokidar": "^3.5.2", diff --git a/src/age/age.ts b/src/age/age.ts index 3f0e06f6..b3c98462 100644 --- a/src/age/age.ts +++ b/src/age/age.ts @@ -16,8 +16,7 @@ let skipped = Number.MAX_SAFE_INTEGER; // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function load(config: Config | any) { if (!model) { - // @ts-ignore type mismatch on GraphModel - model = await tf.loadGraphModel(join(config.modelBasePath, config.face.age.modelPath)); + model = await tf.loadGraphModel(join(config.modelBasePath, config.face.age.modelPath)) as unknown as GraphModel; if (!model || !model['modelUrl']) log('load model failed:', config.face.age.modelPath); else if (config.debug) log('load model:', model['modelUrl']); } else if (config.debug) log('cached model:', model['modelUrl']); @@ -33,7 +32,7 @@ export async function predict(image: Tensor, config: Config | any) { } skipped = 0; return new Promise(async (resolve) => { - if (!model.inputs[0].shape) return; + if (!model.inputs || !model.inputs[0] || !model.inputs[0].shape) return; const resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); const enhance = tf.mul(resize, [255.0]); tf.dispose(resize); diff --git a/src/draw/draw.ts b/src/draw/draw.ts index 78406396..aa07fff7 100644 --- a/src/draw/draw.ts +++ b/src/draw/draw.ts @@ -278,16 +278,13 @@ export async function body(inCanvas: HTMLCanvasElement, result: Array, dra ctx.lineWidth = localOptions.lineWidth; ctx.font = localOptions.font; if (localOptions.drawBoxes && result[i].box && result[i].box?.length === 4) { - // @ts-ignore box may not exist rect(ctx, result[i].box[0], result[i].box[1], result[i].box[2], result[i].box[3], localOptions); if (localOptions.drawLabels) { if (localOptions.shadowColor && localOptions.shadowColor !== '') { ctx.fillStyle = localOptions.shadowColor; - // @ts-ignore box may not exist ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 3, 1 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]); } ctx.fillStyle = localOptions.labelColor; - // @ts-ignore box may not exist ctx.fillText(`body ${100 * result[i].score}%`, result[i].box[0] + 2, 0 + result[i].box[1] + localOptions.lineHeight, result[i].box[2]); } } diff --git a/src/efficientpose/efficientpose.ts b/src/efficientpose/efficientpose.ts index 60fa673f..423a9920 100644 --- a/src/efficientpose/efficientpose.ts +++ b/src/efficientpose/efficientpose.ts @@ -22,8 +22,7 @@ const bodyParts = ['head', 'neck', 'rightShoulder', 'rightElbow', 'rightWrist', export async function load(config: Config): Promise { if (!model) { - // @ts-ignore type mismatch on GraphModel - model = await tf.loadGraphModel(join(config.modelBasePath, config.body.modelPath)); + model = await tf.loadGraphModel(join(config.modelBasePath, config.body.modelPath)) as unknown as GraphModel; if (!model || !model['modelUrl']) log('load model failed:', config.body.modelPath); else if (config.debug) log('load model:', model['modelUrl']); } else if (config.debug) log('cached model:', model['modelUrl']); diff --git a/src/face.ts b/src/face.ts index dcf7fc08..557d1089 100644 --- a/src/face.ts +++ b/src/face.ts @@ -162,7 +162,7 @@ export const detectFace = async (parent /* instance of human */, input: Tensor): parent.analyze('Get Face'); // is something went wrong, skip the face - // @ts-ignore possibly undefined + // @ts-ignore possibly undefied if (!faces[i].tensor || faces[i].tensor['isDisposedInternal']) { log('Face object is disposed:', faces[i].tensor); continue; diff --git a/src/faceres/faceres.ts b/src/faceres/faceres.ts index 0164ff9e..6c317ed4 100644 --- a/src/faceres/faceres.ts +++ b/src/faceres/faceres.ts @@ -25,8 +25,7 @@ type DB = Array<{ name: string, source: string, embedding: number[] }>; export async function load(config: Config): Promise { const modelUrl = join(config.modelBasePath, config.face.description.modelPath); if (!model) { - // @ts-ignore type mismatch for GraphModel - model = await tf.loadGraphModel(modelUrl); + model = await tf.loadGraphModel(modelUrl) as unknown as GraphModel; if (!model) log('load model failed:', config.face.description.modelPath); else if (config.debug) log('load model:', modelUrl); } else if (config.debug) log('cached model:', modelUrl); diff --git a/src/gender/gender.ts b/src/gender/gender.ts index 4edec03f..bd294ca1 100644 --- a/src/gender/gender.ts +++ b/src/gender/gender.ts @@ -19,8 +19,7 @@ const rgb = [0.2989, 0.5870, 0.1140]; // factors for red/green/blue colors when // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function load(config: Config | any) { if (!model) { - // @ts-ignore type mismatch on GraphModel - model = await tf.loadGraphModel(join(config.modelBasePath, config.face.gender.modelPath)); + model = await tf.loadGraphModel(join(config.modelBasePath, config.face.gender.modelPath)) as unknown as GraphModel; alternative = model.inputs[0].shape ? model.inputs[0]?.shape[3] === 1 : false; if (!model || !model['modelUrl']) log('load model failed:', config.face.gender.modelPath); else if (config.debug) log('load model:', model['modelUrl']); diff --git a/src/handpose/handdetector.ts b/src/handpose/handdetector.ts index 3a574c6c..eb32de4e 100644 --- a/src/handpose/handdetector.ts +++ b/src/handpose/handdetector.ts @@ -15,8 +15,7 @@ export class HandDetector { this.model = model; this.anchors = anchors.anchors.map((anchor) => [anchor.x, anchor.y]); this.anchorsTensor = tf.tensor2d(this.anchors); - // @ts-ignore model is not undefined here - this.inputSize = this.model?.inputs[0].shape[2]; + this.inputSize = (this.model && this.model.inputs && this.model.inputs[0].shape) ? this.model.inputs[0].shape[2] : 0; this.inputSizeTensor = tf.tensor1d([this.inputSize, this.inputSize]); this.doubleInputSizeTensor = tf.tensor1d([this.inputSize * 2, this.inputSize * 2]); } diff --git a/src/handpose/handpose.ts b/src/handpose/handpose.ts index a410a602..e625a64e 100644 --- a/src/handpose/handpose.ts +++ b/src/handpose/handpose.ts @@ -71,10 +71,9 @@ export async function predict(input: Tensor, config: Config): Promise { export async function load(config: Config): Promise<[GraphModel | null, GraphModel | null]> { if (!handDetectorModel || !handPoseModel) { - // @ts-ignore type mismatch on GraphModel [handDetectorModel, handPoseModel] = await Promise.all([ - config.hand.enabled ? tf.loadGraphModel(join(config.modelBasePath, config.hand.detector.modelPath), { fromTFHub: config.hand.detector.modelPath.includes('tfhub.dev') }) : null, - config.hand.landmarks ? tf.loadGraphModel(join(config.modelBasePath, config.hand.skeleton.modelPath), { fromTFHub: config.hand.skeleton.modelPath.includes('tfhub.dev') }) : null, + config.hand.enabled ? tf.loadGraphModel(join(config.modelBasePath, config.hand.detector.modelPath), { fromTFHub: config.hand.detector.modelPath.includes('tfhub.dev') }) as unknown as GraphModel : null, + config.hand.landmarks ? tf.loadGraphModel(join(config.modelBasePath, config.hand.skeleton.modelPath), { fromTFHub: config.hand.skeleton.modelPath.includes('tfhub.dev') }) as unknown as GraphModel : null, ]); if (config.hand.enabled) { if (!handDetectorModel || !handDetectorModel['modelUrl']) log('load model failed:', config.hand.detector.modelPath); diff --git a/src/helpers.ts b/src/helpers.ts index 4d578cad..0fb93e74 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -41,7 +41,7 @@ export function mergeDeep(...objects) { } // helper function: return min and max from input array -export const minmax = (data) => data.reduce((acc, val) => { +export const minmax = (data: Array) => data.reduce((acc: Array, val) => { acc[0] = (acc[0] === undefined || val < acc[0]) ? val : acc[0]; acc[1] = (acc[1] === undefined || val > acc[1]) ? val : acc[1]; return acc; diff --git a/src/human.ts b/src/human.ts index efdf98d5..331efce9 100644 --- a/src/human.ts +++ b/src/human.ts @@ -4,7 +4,7 @@ import { log, now, mergeDeep } from './helpers'; import { Config, defaults } from './config'; -import { Result, Gesture } from './result'; +import { Result, Face, Hand, Body, Item, Gesture } from './result'; import * as sysinfo from './sysinfo'; import * as tf from '../dist/tfjs.esm.js'; import * as backend from './tfjs/backend'; @@ -166,6 +166,7 @@ export class Human { faceres: null, segmentation: null, }; + this.result = { face: [], body: [], hand: [], gesture: [], object: [], performance: {}, timestamp: 0, persons: [] }; // export access to image processing // @ts-ignore eslint-typescript cannot correctly infer type in anonymous function this.image = (input: Input) => image.process(input, this.config); @@ -179,7 +180,7 @@ export class Human { // helper function: measure tensor leak /** @hidden */ - analyze = (...msg) => { + analyze = (...msg: string[]) => { if (!this.#analyzeMemoryLeaks) return; const currentTensors = this.tf.engine().state.numTensors; const previousTensors = this.#numTensors; @@ -190,7 +191,7 @@ export class Human { // quick sanity check on inputs /** @hidden */ - #sanity = (input): null | string => { + #sanity = (input: Input): null | string => { if (!this.#checkSanity) return null; if (!input) return 'input is not defined'; if (this.tf.ENV.flags.IS_NODE && !(input instanceof tf.Tensor)) return 'input must be a tensor'; @@ -233,7 +234,6 @@ export class Human { */ // eslint-disable-next-line class-methods-use-this enhance(input: Tensor): Tensor | null { - // @ts-ignore type mismach for Tensor return faceres.enhance(input); } @@ -391,9 +391,10 @@ export class Human { // check if input changed sufficiently to trigger new detections /** @hidden */ - #skipFrame = async (input) => { + #skipFrame = async (input: Tensor) => { if (this.config.cacheSensitivity === 0) return false; const resizeFact = 32; + if (!input.shape[1] || !input.shape[2]) return false; const reduced: Tensor = tf.image.resizeBilinear(input, [Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]); // use tensor sum /* @@ -453,23 +454,6 @@ export class Human { // load models if enabled await this.load(); - /* - // function disabled in favor of inputChanged - // disable video optimization for inputs of type image, but skip if inside worker thread - let previousVideoOptimized; - // @ts-ignore ignore missing type for WorkerGlobalScope as that is the point - if (input && this.config.videoOptimized && (typeof window !== 'undefined') && (typeof WorkerGlobalScope !== 'undefined') && ( - (typeof HTMLImageElement !== 'undefined' && input instanceof HTMLImageElement) - || (typeof Image !== 'undefined' && input instanceof Image) - || (typeof ImageData !== 'undefined' && input instanceof ImageData) - || (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap)) - ) { - log('disabling video optimization'); - previousVideoOptimized = this.config.videoOptimized; - this.config.videoOptimized = false; - } - */ - timeStamp = now(); let process = image.process(input, this.config); this.performance.image = Math.trunc(now() - timeStamp); @@ -508,10 +492,10 @@ export class Human { // prepare where to store model results // keep them with weak typing as it can be promise or not - let faceRes; - let bodyRes; - let handRes; - let objectRes; + let faceRes: Face[] | Promise | never[] = []; + let bodyRes: Body[] | Promise | never[] = []; + let handRes: Hand[] | Promise | never[] = []; + let objectRes: Item[] | Promise | never[] = []; // run face detection followed by all models that rely on face bounding box: face mesh, age, gender, emotion if (this.config.async) { @@ -590,15 +574,15 @@ export class Human { this.performance.total = Math.trunc(now() - timeStart); this.state = 'idle'; this.result = { - face: faceRes, - body: bodyRes, - hand: handRes, + face: faceRes as Face[], + body: bodyRes as Body[], + hand: handRes as Hand[], gesture: gestureRes, - object: objectRes, + object: objectRes as Item[], performance: this.performance, canvas: process.canvas, timestamp: Date.now(), - get persons() { return persons.join(faceRes, bodyRes, handRes, gestureRes, process?.tensor?.shape); }, + get persons() { return persons.join(faceRes as Face[], bodyRes as Body[], handRes as Hand[], gestureRes, process?.tensor?.shape); }, }; // finally dispose input tensor @@ -611,7 +595,7 @@ export class Human { /** @hidden */ #warmupBitmap = async () => { - const b64toBlob = (base64, type = 'application/octet-stream') => fetch(`data:${type};base64,${base64}`).then((res) => res.blob()); + const b64toBlob = (base64: string, type = 'application/octet-stream') => fetch(`data:${type};base64,${base64}`).then((res) => res.blob()); let blob; let res; switch (this.config.warmup) { @@ -662,7 +646,7 @@ export class Human { /** @hidden */ #warmupNode = async () => { - const atob = (str) => Buffer.from(str, 'base64'); + const atob = (str: string) => Buffer.from(str, 'base64'); let img; if (this.config.warmup === 'face') img = atob(sample.face); if (this.config.warmup === 'body' || this.config.warmup === 'full') img = atob(sample.body); diff --git a/src/image/image.ts b/src/image/image.ts index 22b7c90c..257ba1d0 100644 --- a/src/image/image.ts +++ b/src/image/image.ts @@ -14,7 +14,7 @@ const maxSize = 2048; let inCanvas; let outCanvas; // instance of fximage -let fx; +let fx: fxImage.GLImageFilter | null; // process input image and return tensor // input can be tensor, imagedata, htmlimageelement, htmlvideoelement diff --git a/src/models.ts b/src/models.ts index c52ffd9d..5975df70 100644 --- a/src/models.ts +++ b/src/models.ts @@ -18,29 +18,17 @@ import * as segmentation from './segmentation/segmentation'; export async function load(instance) { if (instance.config.async) { // load models concurrently [ - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.face, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.emotion, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.handpose, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.posenet, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.blazepose, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.efficientpose, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.movenet, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.nanodet, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.centernet, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.faceres, - // @ts-ignore models loaded via promise array cannot be correctly inferred instance.models.segmentation, - // @ts-ignore models loaded via promise array cannot be correctly inferred // instance.models.agegenderrace, ] = await Promise.all([ instance.models.face || (instance.config.face.enabled ? facemesh.load(instance.config) : null), diff --git a/src/movenet/movenet.ts b/src/movenet/movenet.ts index 0db93490..b4cc29ac 100644 --- a/src/movenet/movenet.ts +++ b/src/movenet/movenet.ts @@ -22,8 +22,7 @@ const bodyParts = ['nose', 'leftEye', 'rightEye', 'leftEar', 'rightEar', 'leftSh export async function load(config: Config): Promise { if (!model) { - // @ts-ignore type mismatch on GraphModel - model = await tf.loadGraphModel(join(config.modelBasePath, config.body.modelPath)); + model = await tf.loadGraphModel(join(config.modelBasePath, config.body.modelPath)) as unknown as GraphModel; if (!model || !model['modelUrl']) log('load model failed:', config.body.modelPath); else if (config.debug) log('load model:', model['modelUrl']); } else if (config.debug) log('cached model:', model['modelUrl']); diff --git a/src/persons.ts b/src/persons.ts index f3359d95..5d3f2671 100644 --- a/src/persons.ts +++ b/src/persons.ts @@ -44,7 +44,7 @@ export function join(faces: Array, bodies: Array, hands: Array // create new overarching box from all boxes beloning to person const x: number[] = []; const y: number[] = []; - const extractXY = (box) => { // extract all [x, y] coordinates from boxes [x, y, width, height] + const extractXY = (box: [number, number, number, number] | undefined) => { // extract all [x, y] coordinates from boxes [x, y, width, height] if (box && box.length === 4) { x.push(box[0], box[0] + box[2]); y.push(box[1], box[1] + box[3]); @@ -59,7 +59,7 @@ export function join(faces: Array, bodies: Array, hands: Array person.box = [minX, minY, Math.max(...x) - minX, Math.max(...y) - minY]; // create new overarching box // shape is known so we calculate boxRaw as well - if (shape && shape.length === 4) person.boxRaw = [person.box[0] / shape[2], person.box[1] / shape[1], person.box[2] / shape[2], person.box[3] / shape[1]]; + if (shape && shape[1] && shape[2]) person.boxRaw = [person.box[0] / shape[2], person.box[1] / shape[1], person.box[2] / shape[2], person.box[3] / shape[1]]; persons.push(person); } diff --git a/src/posenet/utils.ts b/src/posenet/utils.ts index c8383647..097c7a68 100644 --- a/src/posenet/utils.ts +++ b/src/posenet/utils.ts @@ -1,11 +1,11 @@ import * as kpt from './keypoints'; import { Body } from '../result'; -export function eitherPointDoesntMeetConfidence(a, b, minConfidence) { +export function eitherPointDoesntMeetConfidence(a: number, b: number, minConfidence: number) { return (a < minConfidence || b < minConfidence); } -export function getAdjacentKeyPoints(keypoints, minConfidence) { +export function getAdjacentKeyPoints(keypoints, minConfidence: number) { return kpt.connectedPartIndices.reduce((result, [leftJoint, rightJoint]) => { if (eitherPointDoesntMeetConfidence(keypoints[leftJoint].score, keypoints[rightJoint].score, minConfidence)) { return result; diff --git a/tsconfig.json b/tsconfig.json index 64f1ec30..f4f1a543 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,36 +1,46 @@ { "compilerOptions": { - "noEmitOnError": false, "module": "es2020", "target": "es2018", "moduleResolution": "node", "typeRoots": ["node_modules/@types"], "outDir": "types", + "baseUrl": "./", + "paths": { "tslib": ["node_modules/tslib/tslib.d.ts"] }, + "noEmitOnError": false, "declaration": true, + "allowJs": true, "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "alwaysStrict": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": false, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noPropertyAccessFromIndexSignature": false, + "noUncheckedIndexedAccess": false, + "noUnusedLocals": false, + "noUnusedParameters": true, "preserveConstEnums": true, + "pretty": true, "removeComments": false, "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, - "allowJs": true, - "baseUrl": "./", - "paths": { "tslib": ["node_modules/tslib/tslib.d.ts"] }, + "strictBindCallApply": true, + "strictFunctionTypes": true, "strictNullChecks": true, - "noImplicitAny": false, - "noUnusedLocals": false, - "noImplicitReturns": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedParameters": true, - "pretty": true, - "noFallthroughCasesInSwitch": true, - "allowUnreachableCode": false + "strictPropertyInitialization": true + }, + "formatCodeOptions": { + "indentSize": 2, + "tabSize": 2 }, - "formatCodeOptions": { "indentSize": 2, "tabSize": 2 }, "exclude": ["node_modules/", "types/", "tfjs/", "dist/"], "include": ["src"], "typedocOptions": {