🌊 Play the demo 🌊
demo-video.mp4
This is a Pong Wars clone inspired by The Great Wave off Kanagawa.
I stumbled upon this post and immediately wanted to create my own version. I was even more interested when I saw the implementation: it was a single HTML file with some vanilla JS in a script tag; the OG way of building. As a long time Typescript user, I developed a habit of dismissing the idea of building something in vanilla JS, but I wanted to see for myself if I could implement my version with a similar setup, while preserving most of Typescript's ergonomics via JSDoc comments.
Note
tldr; // @ts-check
and JSDoc comments are sometimes all you need.
By utilizing the following tips you can work in the plain JS projects and still get most of the usual Typescript benefits: code autocomplete, navigate to references, safely rename symbols, etc. 😌
Write the // @ts-check
comment before anything else. Place it at the beginning of the HTML's script tag and at the beginning of all JS files. This special comment will be interpreted by your editor, so it will automatically apply the type checking analysis, as if you're writing JS in a TS project. This analysis works out of the box in both VS Code and WebStorm (VS Code works better for script tags), no need to install anything extra; no editor plugins or npm
packages. You don't even need the package.json
for this to work. Find out more here.
You can define new types in JS! This is the reason why I'm writing this whole thing; more people need to know about it. 📣
With JSDoc, you can define custom types and later annotate code with those types. Read more here.
// Define type `State` (the name goes at the end of type declaration).
/**
* @typedef {{
* player1: Player
* player2: Player
* board: Board
* }} State
*/
// Use the `State` type to anotate variables, funcrion params and return types.
/** @type {State} */
const state = {...}
/**
* @param state {State}
*/
function recalculateState(state) {...}
/** @returns {State} */
function getInitialState() {...}
You can now find all usages/references of the State
type, and safely rename its props.
You can even define generic functions.
/**
* @template T
* @param arr {Array<T>}
* @returns {T}
*/
function randomArrayItem (arr) {...}
You can even have function signature overrides. E.g. a RNG function can accept a single argument: a max
, for the range [0, max]
; or it can accept two arguments: min
and max
, for the range [min, max]
.
/**
* @overload
* @param max {number}
* @returns {number}
*/
/**
* @overload
* @param min {number}
* @param max {number}
* @returns {number}
*/
/**
* @param min {number}
* @param [max] {number}
* @returns {number}
*/
function randomFloat (min, max) {...}
At some point you will wish to split your code across multiple JS files. Writing // @ts-check
everywhere and the lack of the control over type checking can get annoying. Luckily you can get many benefits of Typescript by simply creating a tsconfig.josn
file in your project's root; you don't need to install anything — just create the config file, the editor will pick it up.
// `tsconfig.json`
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"allowJs": true,
"checkJs": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true
}
}
Any http server will do. E.g.
npx serve
# or
python3 -m http.server
Feel free to share this with your hacker friends and colleagues. 🙌 🥳