Codeforces - How to use Typescript/Javascript like a pro
Tags: tutorial, computerscience, typescript, algorithms
Published: 2021-03-17 18:00:19

Hi everybody, 

For a TL;DR section just skip to the [end](#TL;DR).

Getting that out of the way, let's get started. So, today I wanted to explore again some algorithmics problems and I returned to CodeForces.

Lately, I have used a lot of Typescript so naturally, I wanted to use it here too and I gladly discovered that you can actually use javascript in it.  

Then the next question was, but how can I run only a single file in my Typescript project that holds many other problems (or at least it will)?

I tried many solutions but I came about creating an `npm package` that I can simply import or require since otherwise, I had issues with imports, paths, and more.

This is the package [codeforces-io](https://www.npmjs.com/package/@ip-algorithmics/codeforces-io).
It is a simple mock for the `readline()` and `print()` functions so you do not have to modify your existing code while copying it to submission.

The documentation covers how to use it as a simple package, so I will write here a tutorial from start to finish with some advanced usages that you might like.

I created this package for those who want their code to be as `close to the submission code` as possible without having to deal with the `boilerplate™` e.g.[1](https://codeforces.com/blog/entry/69610),[2](https://codeforces.com/blog/entry/77741). 
{% github https://github.com/IP-Algorithmics/CodeForcesIO %}

# Too much talk, let's see the usage

First, you have to have an `npm` project in the folder. So open up a terminal in your desired folder and just type `npm init`. You can use the default.

Then `npm i @ip-algorithmics/codeforces-io`

If you do not use Typescript this is all you need to do, otherwise... just run this one after the other.

- `npm i -g typescript ts-node`

Note: if you want to configure Typescript you will additionally need to run
- `tsc --init`

Remember to `not` include the "DOM" library into the `lib` because the `print` function is already declared in the DOM library for printing files. 
You can just require/import the `print` function for this package for output to the console. If need other functions from the console object you can require the entire `console`.

## Usage with `ts-node` or `node`
Now that you have a working environment you can create a `.js` or `.ts` file and get going.
This library uses modern ES6 import/export modules but that means declaring a few more things in the project [configuration](https://stackoverflow.com/questions/45854169/how-can-i-use-an-es6-import-in-node-js).
So let's stay to `require` for now.
At the top of your file just write
```javascript
const {nextLine, print, console} = require('@ip-algorithmics/codeforces-io')
```
Now that we have our functions we can use them in the project.

The `readline` function reads from `input.txt` file that needs to be in the same folder.
Here is a quick example
```javascript
// example.js or example.ts
const { readline, print } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((x) => parseInt(x, 10));
    if (x[0] < 90 || x[0] > 90 || x[1] < 90 || x[1] > 90) {
        print(x);
        break;
    }
    // do something with x
}
```
And here is the `input.txt`
```text
3
-47,23
34,56
91,82
```

Now that we have our files in place we can just run 
`node example.js` or `ts-node example.ts`. You can use an absolute or relative path, this library will search for the place where the script is and then read the input file from there. 
So you do not have to `cd <path>` you can just use `node <path>/<file>`.

## Usage with jest
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khfu2ck23ip565ji9r27.png)
For a simple reason. Paths can get really long and jest can use test names to find the file directly

Compare these:
- `ts-node src/1300-points/totally-real-problem/index.ts`
- `jest -t 'totally-real-problem`
I'd say it's a bit shorter.
But how can you obtain that? 
Well, that is simple actually. 

Just run `npm i -g jest ts-jest` and  `npm i @types/jest` and then in the root of the project you need to create a file named `jest.config.ts` and put the next lines in it:
```typescript
export default {
    preset: 'ts-jest',
    testEnvironment: 'node',
    transform: {
        'node_modules/variables/.+\\.(j|t)sx?$': 'ts-jest'
    },
    transformIgnorePatterns: ['node_modules/(?!variables/.*)']
};

```
For javascript you can just run `jest --init` and that will work by default.

Except for this, now all of your scripts will have to be a test
so the naming should be <name>.test.ts or <name>.spec.ts or create a folder named `__tests__` and place your scripts in it (same things apply to javascript also).

Now you have to convert your scripts to tests. This is as simple as wrapping everything in 
```javascript 
test('name of test', () => { 
// your code
})
```
The example from above will get transformed to this.
```javascript
test('example', () => {
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}
}
```
To run it just type `jest -t 'example'`.

## Special mention 
For typescript projects if you use the same function names without putting them in a class you will get an error that the name is taken, so you have to add `export {};` at the end of the file so the names will not conflict.

If something doesn't work do not hesitate to leave a comment, open an issue or even better create a pull request. 

#<a name="TL;DR">TL;DR</a>
- Created a library for Codeforces that reads `input.txt` from the folder the script was called.
- `npm i @ip-algorithmics/codeforces-io`
- Code example 
```javascript
// example.js or example.ts
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}

```
And here is the input.txt
```text
3
-47,23
34,56
91,82
```
- `node <path to file>/file.js` or `ts-node <path to file>/file.ts`

Happy codding!

# Update
I added the `testOutput` function. Now you can create your `output.txt` file and put the outputs in it. 
You just have to run the function at the end of the script and it will prompt you with a `Passed` or `Failed` output in the console. 
Remember not to copy this function in the submission. 

Internally now the `print` caches everything so that the `testOutput` can check if what was printed is the same as what is in the `output.txt`

Example:
`input.txt`

```text
3
-47,23
34,56
91,82
```

`output.txt`

```text
-47,23
```

```javascript
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}

testOutput(); // Result Passed
```

## Update 2
The solution generator is completed. Now you can just create the `input.txt`, `output.txt`, `index.ts` and the folder with a simple command. It will also add the imports for you.

You can use this feature in 2 ways:
-   Without installing globally the library using `npx @ip-algorithmics/codeforces-io`.
-   Installing globally the library `npm i -g @ip-algorithmics/codeforces-io` and then using `cf` in the command line.

### Parameters

-   `path` - the path to the solution. E.g. `./ProblemA`. Defaults to `./New Solution`.
-   `--f` or `--file` - the name of the js/ts file to create. E.g. `mainFile`. Defaults to `index`.
-   `--js` - Uses `.js` extension instead of `.ts`
-   `--cjs` - Uses `require` instead of ES6 `import`/`export`
-   `--c` or `--comment` - Adds a comment at the beginning of the file. E.g. the link to the problem.

### Usage examples

-   `npx @ip-algorithmics/codeforces-io ./ProblemA --js --cjs`
-   `cf ./ProblemA --js --cjs`
-   `cf ./ProblemA`
-   `cf ./ProblemA --c http://link.to.problem` 

# Update 3 - Typescript ideas
I noticed that Codforces uses ES5 internally in both node and V8 so it has problems with `const`, `let` and features like spreading an array.

Personally I found that the best solution is to:
- add in the `package.json` the following script ` "build": "tsc --module es6 --moduleResolution node"` using it like `npm run build <path>` 
- manually run `tsc --module es6 --moduleResolution node <path>`