React with TypeScript

Tic Tac Toe
Tic Tac Toe

I made a game of Tic Tac Toe on React with TypeScript instead of JavaScript. You can follow this official tutorial of React to build it with JavaScript. Then you can convert it to TypeScript with this handbook.

Tic Tac Toe is a paper-and-pencil game for two players who take turns marking the spaces in a three-by-three grid with X or O. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row is the winner.

React is a front-end JavaScript library for building user interfaces or UI components. It is maintained by Facebook and a community of individual developers and companies. React can be used as a base in the development of single-page or mobile applications. It is a popular front-end framework. Many companies require their front-end developers to master it.

TypeScript is a programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript and adds optional static typing to the language. TypeScript is designed for the development of large applications and transcompiles to JavaScript. As TypeScript is a superset of JavaScript, existing JavaScript programs are also valid TypeScript programs. The TypeScript compiler is itself written in TypeScript and compiled to JavaScript. Anders Hejlsberg, the lead architect of C# and creator of Delphi and Turbo Pascal, has worked on the development of TypeScript.

I think TypeScript is better than JavaScript. It is also a good combination with React as a popular front-end framework. Now, let's code it.

Using TypeScript with Create React App

Let's get started by creating a React app with TypeScript.

npx create-react-app my-app --template typescript

Install Yarn CLI by using the command npm install --global yarn. Then, we can start development by using the command yarn start. It will recompile our React app every time it has some changes.

CSS

We focus on two files; index.tsx and App.tsx. Let's create CSS files for them; index.css and App.css.

1
2
3
4
5
6
7
8
body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

ol, ul {
  padding-left: 30px;
}

The index.css is to reset styles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

The App.css is to style the main app.

TSX

The .ts is the file extension for TypeScript files. The .tsx is used when we want to embed JSX elements inside the files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()

We leave it default for index.tsx. Just make sure it renders the app.

App

We code the main application on the App.tsx.

Interfaces

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface SquareProps {
  value: string,
  onClick: () => void
}

interface BoardProps {
  squares: string[],
  onClick: (i: number) => void,
}

interface GameState {
  history: {
    squares: string[]
  }[],
  stepNumber: number,
  xIsNext: boolean,
}

We created 3 object types; SquareProps, BoardProps, and GameState.

Calculating Winner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const calculateWinner = (squares: string[]): string => {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ]

  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i]

    if (
      squares[a] !== "" &&
      squares[a] === squares[b] &&
      squares[a] === squares[c]
    ) {
      return squares[a]
    }
  }

  return ""
}

Here is the function to calculate the winner.

Square

1
2
3
4
5
6
7
8
9
class Square extends React.Component<SquareProps, {}> {
  render() {
    return (
      <button className="square" onClick={this.props.onClick}>
        {this.props.value}
      </button>
    )
  }
}

We just rendered a button as a cell of the board.

Board

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Board extends React.Component<BoardProps, {}> {
  constructor(props: any) {
    super(props)

    this.state = {
      squares: Array(9).fill(""),
      xIsNext: true,
    }
  }

  renderSquare(i: number) {
    return <Square
      value={this.props.squares[i]}
      onClick={() => this.props.onClick(i)}
    />
  }

  render() {
    return (
      <div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    )
  }
}

The board has 9 cells or squares.

Game

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class Game extends React.Component<{}, GameState> {
  constructor(props: any) {
    super(props)

    this.state = {
      history: [{
        squares: Array(9).fill(""),
      }],
      stepNumber: 0,
      xIsNext: true,
    }
  }

  handleClick(i: number) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1)
    const current = history[history.length - 1]
    const squares = current.squares.slice()

    if (calculateWinner(squares) || squares[i]) {
      return
    }

    squares[i] = this.state.xIsNext ? "X" : "O"

    this.setState({
      history: history.concat([{
        squares: squares
      }]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext,
    })
  }

  jumpTo(step: number) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    })
  }

  render() {
    const history = this.state.history
    const current = history[this.state.stepNumber]
    const winner = calculateWinner(current.squares)
    const moves = history.map((_, move) => {
      const desc = move ?
        'Go to move #' + move :
        'Go to game start'

      return (
        <li key={move}>
          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      )
    })
    let status: string

    if (winner) {
      status = 'Winner: ' + winner
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O')
    }

    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i: number) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
        </div>
      </div>
    )
  }
}

const App = () => {
  return <Game />
}

We render the board or game, set history, handle a click, then calculate the winner on every move or every time the square got clicked.

Closing

Mastering React and TypeScript is the best choice to be a Front-End expert. React is so popular even some companies put it on the interview. TypeScript is the future and the best choice to handle large projects or work with many developers because it is the only strongly typed programming language I have known for the front-end.

I suggest you master TypeScript first before going further with its development. At least you had a TypeScript crash course. Then you will be able to master its frameworks such as React and Angular.

Maybe I will make a crash course for TypeScript in the future. You can find the source code of this tutorial at https://github.com/aristorinjuang/typescript-react.

References