# What the heck is a tsconfig.json file?

Writing code in TypeScript is beneficial, but as a senior developer, understanding how to configure your project to meet your needs is crucial. One place where you can fine-tune project configuration and inform the TypeScript compiler how to compile the code is through a `tsconfig.json` file. While a default `tsconfig.json` file is provided when setting up a project (`tsc --init`), it may not always suit your requirements. In this article, I'll discuss some of the most commonly used options in a TypeScript configuration file.

`target`

This property specifies the ECMAScript target version for TypeScript compilation. The value can be any valid ECMAScript version like "ES5", "ES6", "ES2015", "ES2016", etc. It determines the level of compatibility with JavaScript engines and which features and syntax TypeScript will allow. We should choose this based on the packages that we are going to use in our project

`strict`

This is one of the most important options in a tsconfig.json file. This decides how strict your project configuration is and this makes a lot of difference in the quality of your project code. When set to `true`, it enables all strict type-checking options: `noImplicitAny`, `noImplicitThis`, `alwaysStrict`, `strictNullChecks`, `strictFunctionTypes`, etc. Enabling strict mode helps catch more type-related errors at compile-time and promotes writing safer and more predictable code. Let us discuss about each type-checking option and the error it throws if made true

* `noImplicitAny`
    
    The `noImplicitAny` compiler option in TypeScript is a strict type-checking option that helps enforce stronger typing discipline by flagging variables and parameters with an implicit `any` type. Let us discuss with an example
    
    ```javascript
    /**
    add function without noImplicitAny in compiler options
    */
    function add(a, b) {
        return a + b;
    }
    
    const result = add(3, "5"); // No compilation error if noImplicitAny is disabled
    console.log(result);
    
    /**
    add function with noImplicitAny in compiler options
    */
    function add(a: number, b: number): number {
        return a + b;
    }
    
    const result = add(3, "5"); // Compilation error: Argument of type 'string' is not assignable to parameter of type 'number'
    console.log(result);
    ```
    
    In the above example, when the function is written without noImplicitAny option disabled, it implicitly considers arguments a and b as type any that leads to not throwing any compilation errors. In the 2nd case, when noImplicitAny is enabled, the compiler asks you to explicitly declare types of arguments which donot allow any other types apart from numbers.
    
* `noImplicitThis`  
    When `noImplicitThis` is enabled, TypeScript will issue a compilation error whenever it detects that the type of `this` is implicitly of type `any`. This often occurs when `this` is used in a function or method without a clear type annotation or without being explicitly bound to a particular context.
    
    By default, TypeScript assumes that the type of `this` within a function or method is `any` if no type information is provided. This can lead to errors, especially in object-oriented code where the context of `this` is crucial for correct behavior. Consider the following example
    
* ```javascript
        class MyClass {
            private data: number = 10;
        
            fetchData() {
                return this.data;
            }
        }
        
        const myObject = new MyClass();
        const fetchDataFunc = myObject.fetchData;
        console.log(fetchDataFunc()); // undefined or runtime error because 'this' is not bound
    ```
    
    In this example, when `fetchDataFunc` is called, the context of `this` is lost because it's invoked as a standalone function rather than as a method of an instance of `MyClass`. As a result, [`this.data`](http://this.data) within `fetchData` would likely result in `undefined`, leading to unexpected behavior or runtime errors.
    
    By enabling `noImplicitThis` in the `tsconfig.json` file TypeScript will raise a compilation error for the usage of `this` in `fetchData`, indicating that the type of `this` is implicitly treated as `any`. Developers must then either provide explicit type annotations or ensure that `this` is correctly bound within the function.
    
    To resolve the compilation error in the previous example, developers could either bind `fetchData` to an instance of `MyClass` or use arrow function syntax, which automatically captures the lexical `this`
    

```javascript
class MyClass {
    private data: number = 10;

    fetchData = () => {
        return this.data;
    }
}

const myObject = new MyClass();
const fetchDataFunc = myObject.fetchData;
console.log(fetchDataFunc()); // 10
```

* `strictNullChecks`
    
    When `strictNullChecks` is enabled,
    
    * Variables declared without an explicit type or initialized to `undefined` are considered to have a type of `undefined`
        
    * Variables declared with a union type that includes `null` or `undefined` cannot be accessed without first checking for null or undefined.
        
    * TypeScript raises compilation errors when null or undefined values are assigned to variables that do not allow them.
        
        Here is an example
        
        ```javascript
        // Example 1: Variable Initialization
        
        let x: number;
        x = 10; // OK
        x = undefined; // Error: Type 'undefined' is not assignable to type 'number'
        
        // Example 2: Union Types
        
        let y: string | null;
        y = "hello"; // OK
        y = null; // OK
        console.log(y.length); // Error: Object is possibly 'null'
        
        // Example 3: Function Parameters
        
        function greet(name: string) {
            return "Hello, " + name;
        }
        
        greet("Alice"); // OK
        greet(null); // Error: Argument of type 'null' is not assignable to parameter of type 'string'
        
        // Example 4: Optional Parameters
        
        function printMessage(message?: string) {
            console.log(message.toUpperCase()); // Error: Object is possibly 'undefined'
        }
        
        printMessage(); // OK
        printMessage("Hello TypeScript"); // OK
        printMessage(undefined); // OK
        ```
        
* `strictFunctionTypes`
    
    When `strictFunctionTypes` is enabled, TypeScript performs stricter checks on function type compatibility, which includes checking parameter types contravariantly (input types are contravariant) and return types covariantly (output types are covariant). This means:
    
    ```javascript
    interface Animal {
        name: string;
    }
    
    interface Dog extends Animal {
        breed: string;
    }
    
    // Function that takes an Animal parameter
    function printAnimal(animal: Animal): void {
        console.log(animal.name);
    }
    
    // Function that takes a Dog parameter
    function printDog(dog: Dog): void {
        console.log(dog.name);
    }
    
    // Assigning 'printDog' to a variable of type 'Animal => void'
    const printFunc: (animal: Animal) => void = printDog; // Error: Argument of type '(dog: Dog) => void' is not assignable to parameter of type '(animal: Animal) => void'.
    ```
    
    In this example, even though `Dog` is a subtype of `Animal`, TypeScript raises an error because `strictFunctionTypes` enforces stricter type checking on function assignments. `printDog` cannot be assigned to a variable of type `(animal: Animal) => void` because its parameter type (`Dog`) is not assignable to the parameter type (`Animal`) of the target function signature.
    
    ```javascript
    // Function that returns a subtype
    function getDog(): Dog {
        return { name: 'Buddy', breed: 'Labrador' };
    }
    
    // Function that returns a supertype
    function getAnimal(): Animal {
        return { name: 'Buddy' };
    }
    
    // Assigning 'getDog' to a variable of type '() => Animal'
    const getFunc: () => Animal = getDog; // OK
    ```
    
    In this example, `strictFunctionTypes` allows assigning `getDog` to a variable of type `() => Animal` because the return type (`Dog`) of `getDog` is assignable to the return type (`Animal`) of the target function signature.  
    By enabling `strictFunctionTypes`, TypeScript ensures stricter type compatibility between function types, which helps catch potential type errors early in the development process, leading to more robust and reliable code. However, it may require adjustments in code where looser function type compatibility was previously tolerated.
    

While there are many other properties, I am not writing about all of them. The above mentioned are the most commonly used properties.

`outDir`:

* Defines the directory where TypeScript compiler outputs compiled JavaScript files.
    
* It can be an absolute or relative path.
    
* All compiled JavaScript files will be placed in this directory while maintaining the same relative directory structure as the source TypeScript files.
    

`module`:

* Specifies the module system used in the generated JavaScript code.
    
* Common values include `"CommonJS"`, `"AMD"`, `"UMD"`, `"System"`, `"ES6"`, and `"ESNext"`.
    
* The choice of module system affects how TypeScript generates module import/export statements in the output JavaScript files.
    

`esModuleInterop`:

* Without `esModuleInterop`, when importing a CommonJS module with a default export using the syntax `import x from 'module'`, TypeScript generates code that expects the module to export its default value as `module.exports.default`.
    
* With `esModuleInterop` enabled, TypeScript generates code that directly accesses the default export, allowing you to use the default import syntax without errors.
    
* Without `esModuleInterop`, when exporting a value as default in a CommonJS module using `module.exports = value`, TypeScript generates code that exports the default value as `exports.default`.
    
* With `esModuleInterop` enabled, TypeScript generates code that exports the default value directly, allowing seamless consumption of default exports by ES modules.
    

There are many other properties that you can explore on the official typescript documentation but these are the most commonly used options that everybody should understand to become a good typescript developer.

Iam leaving the tsconfig.json file setup in my recent project so that it could be a good starting point.

```javascript
{
  "compilerOptions": {
    "target":"ESNext",
    "module":"esnext",
    "allowJs":false,
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "noImplicitThis":true,
    "strictFunctionTypes":true,
    "strictBindCallApply": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "strictPropertyInitialization":true,
    "esModuleInterop": true
  }
}
```
