build: enable noImplicitOverride TypeScript option

The `noImplicitOverride` TypeScript option improves code quality by ensuring that properties from base classes are not accidentally overriden.
Reference: https://www.typescriptlang.org/tsconfig#noImplicitOverride
This commit is contained in:
Charles Lyding 2021-06-13 11:46:45 -04:00 committed by Charles
parent 8be19dada4
commit 9afe185fc6
33 changed files with 102 additions and 99 deletions

View File

@ -31,9 +31,9 @@ import { Schema as AddCommandSchema } from './add';
const npa = require('npm-package-arg');
export class AddCommand extends SchematicCommand<AddCommandSchema> {
readonly allowPrivateSchematics = true;
override readonly allowPrivateSchematics = true;
async initialize(options: AddCommandSchema & Arguments) {
override async initialize(options: AddCommandSchema & Arguments) {
if (options.registry) {
return super.initialize({ ...options, packageRegistry: options.registry });
} else {
@ -235,7 +235,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {
return this.executeSchematic(collectionName, options['--']);
}
async reportAnalytics(
override async reportAnalytics(
paths: string[],
options: AddCommandSchema & Arguments,
dimensions: (boolean | number | string)[] = [],

View File

@ -11,9 +11,9 @@ import { Arguments } from '../models/interface';
import { Schema as BuildCommandSchema } from './build';
export class BuildCommand extends ArchitectCommand<BuildCommandSchema> {
public readonly target = 'build';
public override readonly target = 'build';
public async run(options: ArchitectCommandOptions & Arguments) {
public override async run(options: ArchitectCommandOptions & Arguments) {
return this.runArchitectTarget(options);
}
}

View File

@ -25,10 +25,12 @@ Find more packages on npm https://www.npmjs.com/search?q=ng%20deploy
`;
export class DeployCommand extends ArchitectCommand<DeployCommandSchema> {
public readonly target = 'deploy';
public readonly missingTargetError = BuilderMissing;
public override readonly target = 'deploy';
public override readonly missingTargetError = BuilderMissing;
public async initialize(options: DeployCommandSchema & Arguments): Promise<number | void> {
public override async initialize(
options: DeployCommandSchema & Arguments,
): Promise<number | void> {
if (!options.help) {
return super.initialize(options);
}

View File

@ -11,9 +11,9 @@ import { Arguments } from '../models/interface';
import { Schema as E2eCommandSchema } from './e2e';
export class E2eCommand extends ArchitectCommand<E2eCommandSchema> {
public readonly target = 'e2e';
public readonly multiTarget = true;
public readonly missingTargetError = `
public override readonly target = 'e2e';
public override readonly multiTarget = true;
public override readonly missingTargetError = `
Cannot find "e2e" target for the specified project.
You should add a package that implements end-to-end testing capabilities.
@ -26,7 +26,7 @@ For example:
More options will be added to the list as they become available.
`;
async initialize(options: E2eCommandSchema & Arguments) {
override async initialize(options: E2eCommandSchema & Arguments) {
if (!options.help) {
return super.initialize(options);
}

View File

@ -11,9 +11,9 @@ import { Arguments } from '../models/interface';
import { Schema as ExtractI18nCommandSchema } from './extract-i18n';
export class ExtractI18nCommand extends ArchitectCommand<ExtractI18nCommandSchema> {
public readonly target = 'extract-i18n';
public override readonly target = 'extract-i18n';
public async run(options: ExtractI18nCommandSchema & Arguments) {
public override async run(options: ExtractI18nCommandSchema & Arguments) {
const version = process.version.substr(1).split('.');
if (Number(version[0]) === 12 && Number(version[1]) === 0) {
this.logger.error(

View File

@ -16,7 +16,7 @@ export class GenerateCommand extends SchematicCommand<GenerateCommandSchema> {
// Allows us to resolve aliases before reporting analytics
longSchematicName: string | undefined;
async initialize(options: GenerateCommandSchema & Arguments) {
override async initialize(options: GenerateCommandSchema & Arguments) {
// Fill up the schematics property of the command description.
const [collectionName, schematicName] = await this.parseSchematicInfo(options);
this.collectionName = collectionName;
@ -75,7 +75,7 @@ export class GenerateCommand extends SchematicCommand<GenerateCommandSchema> {
});
}
async reportAnalytics(
override async reportAnalytics(
paths: string[],
options: GenerateCommandSchema & Arguments,
): Promise<void> {
@ -104,7 +104,7 @@ export class GenerateCommand extends SchematicCommand<GenerateCommandSchema> {
return [collectionName, schematicName];
}
public async printHelp() {
public override async printHelp() {
await super.printHelp();
this.logger.info('');

View File

@ -20,11 +20,11 @@ For example:
`;
export class LintCommand extends ArchitectCommand<LintCommandSchema> {
readonly target = 'lint';
readonly multiTarget = true;
readonly missingTargetError = MissingBuilder;
override readonly target = 'lint';
override readonly multiTarget = true;
override readonly missingTargetError = MissingBuilder;
async initialize(options: LintCommandSchema & Arguments): Promise<number | void> {
override async initialize(options: LintCommandSchema & Arguments): Promise<number | void> {
if (!options.help) {
return super.initialize(options);
}

View File

@ -12,10 +12,10 @@ import { VERSION } from '../models/version';
import { Schema as NewCommandSchema } from './new';
export class NewCommand extends SchematicCommand<NewCommandSchema> {
public readonly allowMissingWorkspace = true;
schematicName = 'ng-new';
public override readonly allowMissingWorkspace = true;
override schematicName = 'ng-new';
async initialize(options: NewCommandSchema & Arguments) {
override async initialize(options: NewCommandSchema & Arguments) {
this.collectionName = options.collection || (await this.getDefaultSchematicCollection());
return super.initialize(options);

View File

@ -11,7 +11,7 @@ import { Arguments } from '../models/interface';
import { Schema as RunCommandSchema } from './run';
export class RunCommand extends ArchitectCommand<RunCommandSchema> {
public async run(options: ArchitectCommandOptions & Arguments) {
public override async run(options: ArchitectCommandOptions & Arguments) {
if (options.target) {
return this.runArchitectTarget(options);
} else {

View File

@ -11,13 +11,13 @@ import { Arguments } from '../models/interface';
import { Schema as ServeCommandSchema } from './serve';
export class ServeCommand extends ArchitectCommand<ServeCommandSchema> {
public readonly target = 'serve';
public override readonly target = 'serve';
public validate() {
return true;
}
public async run(options: ArchitectCommandOptions & Arguments) {
public override async run(options: ArchitectCommandOptions & Arguments) {
return this.runArchitectTarget(options);
}
}

View File

@ -11,10 +11,10 @@ import { Arguments } from '../models/interface';
import { Schema as TestCommandSchema } from './test';
export class TestCommand extends ArchitectCommand<TestCommandSchema> {
public readonly target = 'test';
public readonly multiTarget = true;
public override readonly target = 'test';
public override readonly multiTarget = true;
public async run(options: ArchitectCommandOptions & Arguments) {
public override async run(options: ArchitectCommandOptions & Arguments) {
return this.runArchitectTarget(options);
}
}

View File

@ -63,11 +63,11 @@ const disableVersionCheck =
disableVersionCheckEnv.toLowerCase() !== 'false';
export class UpdateCommand extends Command<UpdateCommandSchema> {
public readonly allowMissingWorkspace = true;
public override readonly allowMissingWorkspace = true;
private workflow!: NodeWorkflow;
private packageManager = PackageManager.Npm;
async initialize(options: UpdateCommandSchema & Arguments) {
override async initialize(options: UpdateCommandSchema & Arguments) {
this.packageManager = await getPackageManager(this.context.root);
this.workflow = new NodeWorkflow(this.context.root, {
packageManager: this.packageManager,

View File

@ -28,7 +28,7 @@ export abstract class ArchitectCommand<
protected _architect!: Architect;
protected _architectHost!: WorkspaceNodeModulesArchitectHost;
protected _registry!: json.schema.SchemaRegistry;
protected readonly useReportAnalytics = false;
protected override readonly useReportAnalytics = false;
// If this command supports running multiple targets.
protected multiTarget = false;
@ -36,7 +36,7 @@ export abstract class ArchitectCommand<
target: string | undefined;
missingTargetError: string | undefined;
public async initialize(options: T & Arguments): Promise<number | void> {
public override async initialize(options: T & Arguments): Promise<number | void> {
this._registry = new json.schema.CoreSchemaRegistry();
this._registry.addPostTransform(json.schema.transforms.addUndefinedDefaults);
this._registry.useXDeprecatedProvider((msg) => this.logger.warn(msg));

View File

@ -56,10 +56,10 @@ export class UnknownCollectionError extends Error {
}
export abstract class SchematicCommand<
T extends BaseSchematicSchema & BaseCommandOptions
T extends BaseSchematicSchema & BaseCommandOptions,
> extends Command<T> {
protected readonly allowPrivateSchematics: boolean = false;
protected readonly useReportAnalytics = false;
protected override readonly useReportAnalytics = false;
protected _workflow!: NodeWorkflow;
protected defaultCollectionName = '@schematics/angular';
@ -70,7 +70,7 @@ export abstract class SchematicCommand<
super(context, description, logger);
}
public async initialize(options: T & Arguments) {
public override async initialize(options: T & Arguments) {
await this.createWorkflow(options);
if (this.schematicName) {
@ -94,7 +94,7 @@ export abstract class SchematicCommand<
}
}
public async printHelp() {
public override async printHelp() {
await super.printHelp();
this.logger.info('');
@ -138,7 +138,7 @@ export abstract class SchematicCommand<
return 0;
}
async printHelpUsage() {
override async printHelpUsage() {
const subCommandOption = this.description.options.filter((x) => x.subcommands)[0];
if (!subCommandOption || !subCommandOption.subcommands) {

View File

@ -51,7 +51,7 @@ function shouldWrapSchematic(schematicFile: string): boolean {
}
export class SchematicEngineHost extends NodeModulesEngineHost {
protected _resolveReferenceString(refString: string, parentPath: string) {
protected override _resolveReferenceString(refString: string, parentPath: string) {
const [path, name] = refString.split('#', 2);
// Mimic behavior of ExportStringRef class used in default behavior
const fullPath = path[0] === '.' ? resolve(parentPath ?? process.cwd(), path) : path;

View File

@ -21,7 +21,7 @@ import {
} from '../models/interface';
export class CommandJsonPathException extends BaseException {
constructor(public readonly path: string, public readonly name: string) {
constructor(public readonly path: string, public override readonly name: string) {
super(`File ${path} was not found while constructing the subcommand ${name}.`);
}
}
@ -36,7 +36,7 @@ function _getEnumFromValue<E, T extends E[keyof E]>(
}
if (Object.values(enumeration).includes(value)) {
return (value as unknown) as T;
return value as unknown as T;
}
return defaultValue;

View File

@ -215,7 +215,7 @@ class ArchitectBuilderJobRegistry implements BuilderRegistry {
* A JobRegistry that resolves targets from the host.
*/
class ArchitectTargetJobRegistry extends ArchitectBuilderJobRegistry {
get<A extends json.JsonObject, I extends BuilderInput, O extends BuilderOutput>(
override get<A extends json.JsonObject, I extends BuilderInput, O extends BuilderOutput>(
name: string,
): Observable<experimental.jobs.JobHandler<A, I, O> | null> {
const m = name.match(/^{([^:]+):([^:]+)(?::([^:]*))?}$/i);

View File

@ -34,7 +34,7 @@ export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
throw new Error('compilation is undefined.');
}
constructor(readonly options: IndexHtmlWebpackPluginOptions) {
constructor(override readonly options: IndexHtmlWebpackPluginOptions) {
super(options);
}
@ -101,13 +101,13 @@ export class IndexHtmlWebpackPlugin extends IndexHtmlGenerator {
};
}
async readAsset(path: string): Promise<string> {
override async readAsset(path: string): Promise<string> {
const data = this.compilation.assets[basename(path)].source();
return typeof data === 'string' ? data : data.toString();
}
protected async readIndex(path: string): Promise<string> {
protected override async readIndex(path: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
this.compilation.inputFileSystem.readFile(
path,

View File

@ -17,7 +17,7 @@ import { NodeJsSyncHost } from '../host';
*/
export class TempScopedNodeJsSyncHost extends virtualFs.ScopedHost<fs.Stats> {
protected _sync?: virtualFs.SyncDelegateHost<fs.Stats>;
protected _root: Path;
protected override _root: Path;
constructor() {
const root = normalize(path.join(os.tmpdir(), `devkit-host-${+Date.now()}-${process.pid}`));

View File

@ -11,18 +11,18 @@ import { LogLevel, Logger } from './logger';
export class LevelTransformLogger extends Logger {
constructor(
public readonly name: string,
public readonly parent: Logger | null = null,
public override readonly name: string,
public override readonly parent: Logger | null = null,
public readonly levelTransform: (level: LogLevel) => LogLevel,
) {
super(name, parent);
}
log(level: LogLevel, message: string, metadata: JsonObject = {}): void {
override log(level: LogLevel, message: string, metadata: JsonObject = {}): void {
return super.log(this.levelTransform(level), message, metadata);
}
createChild(name: string): Logger {
override createChild(name: string): Logger {
return new LevelTransformLogger(name, this, this.levelTransform);
}
}
@ -37,8 +37,8 @@ export class LevelCapLogger extends LevelTransformLogger {
};
constructor(
public readonly name: string,
public readonly parent: Logger | null = null,
public override readonly name: string,
public override readonly parent: Logger | null = null,
public readonly levelCap: LogLevel,
) {
super(name, parent, (level: LogLevel) => {

View File

@ -134,22 +134,22 @@ export class Logger extends Observable<LogEntry> implements LoggerApi {
return this.log('fatal', message, metadata);
}
toString() {
override toString() {
return `<Logger(${this.name})>`;
}
lift<R>(operator: Operator<LogEntry, R>): Observable<R> {
override lift<R>(operator: Operator<LogEntry, R>): Observable<R> {
return this._observable.lift(operator);
}
subscribe(): Subscription;
subscribe(observer: PartialObserver<LogEntry>): Subscription;
subscribe(
override subscribe(): Subscription;
override subscribe(observer: PartialObserver<LogEntry>): Subscription;
override subscribe(
next?: (value: LogEntry) => void,
error?: (error: Error) => void,
complete?: () => void,
): Subscription;
subscribe(
override subscribe(
_observerOrNext?: PartialObserver<LogEntry> | ((value: LogEntry) => void),
_error?: (error: Error) => void,
_complete?: () => void,
@ -158,10 +158,10 @@ export class Logger extends Observable<LogEntry> implements LoggerApi {
return this._observable.subscribe.apply(
this._observable,
// eslint-disable-next-line prefer-rest-params
(arguments as unknown) as Parameters<Observable<LogEntry>['subscribe']>,
arguments as unknown as Parameters<Observable<LogEntry>['subscribe']>,
);
}
forEach(next: (value: LogEntry) => void, PromiseCtor?: typeof Promise): Promise<void> {
override forEach(next: (value: LogEntry) => void, PromiseCtor?: typeof Promise): Promise<void> {
return this._observable.forEach(next, PromiseCtor);
}
}

View File

@ -15,7 +15,7 @@ export class NullLogger extends Logger {
this._observable = EMPTY;
}
asApi(): LoggerApi {
override asApi(): LoggerApi {
return {
createChild: () => new NullLogger(this),
log() {},

View File

@ -70,7 +70,7 @@ export class CordHost extends SimpleMemoryHost {
get backend(): ReadonlyHost {
return this._back;
}
get capabilities(): HostCapabilities {
override get capabilities(): HostCapabilities {
// Our own host is always Synchronous, but the backend might not be.
return {
synchronous: this._back.capabilities.synchronous,
@ -219,7 +219,7 @@ export class CordHost extends SimpleMemoryHost {
);
}
write(path: Path, content: FileBuffer): Observable<void> {
override write(path: Path, content: FileBuffer): Observable<void> {
return this.exists(path).pipe(
switchMap((exists) => {
if (exists) {
@ -236,7 +236,7 @@ export class CordHost extends SimpleMemoryHost {
);
}
read(path: Path): Observable<FileBuffer> {
override read(path: Path): Observable<FileBuffer> {
if (this._exists(path)) {
return super.read(path);
}
@ -244,7 +244,7 @@ export class CordHost extends SimpleMemoryHost {
return this._back.read(path);
}
delete(path: Path): Observable<void> {
override delete(path: Path): Observable<void> {
if (this._exists(path)) {
if (this._filesToCreate.has(path)) {
this._filesToCreate.delete(path);
@ -280,7 +280,7 @@ export class CordHost extends SimpleMemoryHost {
}
}
rename(from: Path, to: Path): Observable<void> {
override rename(from: Path, to: Path): Observable<void> {
return concat(this.exists(to), this.exists(from)).pipe(
toArray(),
switchMap(([existTo, existFrom]) => {
@ -347,7 +347,7 @@ export class CordHost extends SimpleMemoryHost {
);
}
list(path: Path): Observable<PathFragment[]> {
override list(path: Path): Observable<PathFragment[]> {
return concat(super.list(path), this._back.list(path)).pipe(
reduce((list: Set<PathFragment>, curr: PathFragment[]) => {
curr.forEach((elem) => list.add(elem));
@ -358,17 +358,17 @@ export class CordHost extends SimpleMemoryHost {
);
}
exists(path: Path): Observable<boolean> {
override exists(path: Path): Observable<boolean> {
return this._exists(path)
? of(true)
: this.willDelete(path) || this.willRename(path)
? of(false)
: this._back.exists(path);
}
isDirectory(path: Path): Observable<boolean> {
override isDirectory(path: Path): Observable<boolean> {
return this._exists(path) ? super.isDirectory(path) : this._back.isDirectory(path);
}
isFile(path: Path): Observable<boolean> {
override isFile(path: Path): Observable<boolean> {
return this._exists(path)
? super.isFile(path)
: this.willDelete(path) || this.willRename(path)
@ -376,7 +376,7 @@ export class CordHost extends SimpleMemoryHost {
: this._back.isFile(path);
}
stat(path: Path): Observable<Stats | null> | null {
override stat(path: Path): Observable<Stats | null> | null {
return this._exists(path)
? super.stat(path)
: this.willDelete(path) || this.willRename(path)
@ -384,7 +384,7 @@ export class CordHost extends SimpleMemoryHost {
: this._back.stat(path);
}
watch(path: Path, options?: HostWatchOptions) {
override watch(path: Path, options?: HostWatchOptions) {
// Watching not supported.
return null;
}

View File

@ -88,52 +88,52 @@ export namespace test {
}
// Override parents functions to keep a record of all operators that were done.
protected _write(path: Path, content: FileBuffer) {
protected override _write(path: Path, content: FileBuffer) {
this._records.push({ kind: 'write', path });
return super._write(path, content);
}
protected _read(path: Path) {
protected override _read(path: Path) {
this._records.push({ kind: 'read', path });
return super._read(path);
}
protected _delete(path: Path) {
protected override _delete(path: Path) {
this._records.push({ kind: 'delete', path });
return super._delete(path);
}
protected _rename(from: Path, to: Path) {
protected override _rename(from: Path, to: Path) {
this._records.push({ kind: 'rename', from, to });
return super._rename(from, to);
}
protected _list(path: Path): PathFragment[] {
protected override _list(path: Path): PathFragment[] {
this._records.push({ kind: 'list', path });
return super._list(path);
}
protected _exists(path: Path) {
protected override _exists(path: Path) {
this._records.push({ kind: 'exists', path });
return super._exists(path);
}
protected _isDirectory(path: Path) {
protected override _isDirectory(path: Path) {
this._records.push({ kind: 'isDirectory', path });
return super._isDirectory(path);
}
protected _isFile(path: Path) {
protected override _isFile(path: Path) {
this._records.push({ kind: 'isFile', path });
return super._isFile(path);
}
protected _stat(path: Path): Stats<SimpleMemoryHostStats> | null {
protected override _stat(path: Path): Stats<SimpleMemoryHostStats> | null {
this._records.push({ kind: 'stat', path });
return super._stat(path);
}
protected _watch(path: Path, options?: HostWatchOptions): Observable<HostWatchEvent> {
protected override _watch(path: Path, options?: HostWatchOptions): Observable<HostWatchEvent> {
this._records.push({ kind: 'watch', path });
return super._watch(path, options);

View File

@ -190,7 +190,7 @@ export class ProjectDefinitionCollection extends DefinitionCollection<ProjectDef
return project;
}
set(name: string, value: ProjectDefinition): this {
override set(name: string, value: ProjectDefinition): this {
this._validateName(name);
super.set(name, value);
@ -235,7 +235,7 @@ export class TargetDefinitionCollection extends DefinitionCollection<TargetDefin
return target;
}
set(name: string, value: TargetDefinition): this {
override set(name: string, value: TargetDefinition): this {
this._validateName(name);
super.set(name, value);

View File

@ -65,14 +65,14 @@ export class DryRunSink extends HostSink {
);
}
protected _fileAlreadyExistException(path: string): void {
protected override _fileAlreadyExistException(path: string): void {
this._fileAlreadyExistExceptionSet.add(path);
}
protected _fileDoesNotExistException(path: string): void {
protected override _fileDoesNotExistException(path: string): void {
this._fileDoesNotExistExceptionSet.add(path);
}
_done() {
override _done() {
this._fileAlreadyExistExceptionSet.forEach((path) => {
this._subject.next({
kind: 'error',

View File

@ -29,7 +29,7 @@ export class HostSink extends SimpleSinkBase {
super();
}
protected _validateCreateAction(action: CreateFileAction): Observable<void> {
protected override _validateCreateAction(action: CreateFileAction): Observable<void> {
return this._force ? EMPTY : super._validateCreateAction(action);
}

View File

@ -75,15 +75,15 @@ export class UpdateRecorderBom extends UpdateRecorderBase {
super(entry);
}
insertLeft(index: number, content: Buffer | string) {
override insertLeft(index: number, content: Buffer | string) {
return super.insertLeft(index + this._delta, content);
}
insertRight(index: number, content: Buffer | string) {
override insertRight(index: number, content: Buffer | string) {
return super.insertRight(index + this._delta, content);
}
remove(index: number, length: number) {
override remove(index: number, length: number) {
return super.remove(index + this._delta, length);
}
}

View File

@ -101,7 +101,7 @@ export class ScopedTree implements Tree {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const delegate = new (class extends DelegateTree {
get actions(): Action[] {
override get actions(): Action[] {
return other.actions.map((action) => self._fullPathAction(action));
}
})(other);

View File

@ -85,7 +85,7 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase {
return desc as FileSystemSchematicDesc;
}
hasTaskExecutor(name: string): boolean {
override hasTaskExecutor(name: string): boolean {
if (super.hasTaskExecutor(name)) {
return true;
}
@ -100,7 +100,7 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase {
return false;
}
createTaskExecutor(name: string): Observable<TaskExecutor> {
override createTaskExecutor(name: string): Observable<TaskExecutor> {
if (!super.hasTaskExecutor(name)) {
try {
const path = require.resolve(join(this._root, name));

View File

@ -30,7 +30,7 @@ export class NodeModulesTestEngineHost extends NodeModulesEngineHost {
this._collections.set(name, path);
}
transformContext(context: FileSystemSchematicContext): FileSystemSchematicContext {
override transformContext(context: FileSystemSchematicContext): FileSystemSchematicContext {
const oldAddTask = context.addTask;
context.addTask = (task: TaskConfigurationGenerator<{}>, dependencies?: Array<TaskId>) => {
this._tasks.push(task.toConfiguration());
@ -41,7 +41,7 @@ export class NodeModulesTestEngineHost extends NodeModulesEngineHost {
return context;
}
protected _resolveCollectionPath(name: string, requester?: string): string {
protected override _resolveCollectionPath(name: string, requester?: string): string {
const maybePath = this._collections.get(name);
if (maybePath) {
return maybePath;

View File

@ -84,10 +84,10 @@ export class NodeWorkflow extends workflow.BaseWorkflow {
this._context = [];
}
get engine(): FileSystemEngine {
override get engine(): FileSystemEngine {
return this._engine as FileSystemEngine;
}
get engineHost(): NodeModulesEngineHost {
override get engineHost(): NodeModulesEngineHost {
return this._engineHost as NodeModulesEngineHost;
}
}

View File

@ -6,6 +6,7 @@
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
"outDir": "./dist",