Canvas art in TypeScript
I ported a JavaScript “random art generator” I created a few years ago over to TypeScript. This post contains some notes from the process.
- See the project: www.chrismytton.com/art
- Check out the code: chrismytton/art on GitHub
I’ve been learning TypeScript recently. As part of that I wanted to dig out some of my old JavaScript projects and port them to TypeScript. I find porting code to a new language is a good way to learn.
I found an old project that I created in 2014 which uses JavaScript and the Canvas API to create random pieces of art which change on every page load. The code itself is fairly straightforward, so it seemed like a good candidate for porting to TypeScript.
Classes in TypeScript
TypeScript classes build on ES6 classes. The major difference is that in TypeScript you need to explicitly declare the types of properties you’re expecting on the class.
class CanvasPainting {
viewport: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
}
If you try to use this.viewport
without declaring the property TypeScript will tell you:
Property ‘viewport’ does not exist on type ‘CanvasPainting’.
I find having these properties explicitly defined actually makes it easier to figure out what the class might be doing internally. They provide useful documentation about what kind of data you can expect to find in each property.
Index signatures
This is something I struggled with for a while. I was trying to dynamically call methods on my Shapes
class using this[shape]
, but TypeScript was giving this warning:
Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘Shapes’.
No index signature with a parameter of type ‘string’ was found on type ‘Shapes’.
After reading various bits of the TypeScript documentation and some Stack Overflow answers I eventually figured out that I needed to define an index signature on my shapes class. This tells TypeScript what can be used to index this class, and what the indexing might return.
class Shapes extends CanvasPainting {
[key: string]: any;
// ...
}
This tells TypeScript that you can index this class with a string, and it will return something. There might be a way to avoid using any
here, but I haven’t figured that out yet.
Dealing with null values
TypeScript knows which methods can return null
and will make you deal with those cases. If you try to call a method on a value that might be null then TypeScript will give you a warning.
You can avoid this warning by explicitly checking for null
before using the value.
class CanvasPainting {
viewport: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
constructor(viewport: HTMLCanvasElement) {
this.viewport = viewport;
const ctx = viewport.getContext('2d');
// getContext can return null, so check for that case.
if (!ctx) {
throw new Error("getContext('2d') failed");
}
this.ctx = ctx;
}
}
Without this check for null
TypeScript warns you that Type 'null' is not assignable to type 'CanvasRenderingContext2D'.
.
It’s easy to forget to check for null
when writing JavaScript. TypeScript protects you from this class of error at the compile stage.
Summary
I really like what I’ve seen so far with TypeScript. Working on little projects like this is a great way to get some experience with new tools.
I’d forgotten all about this fun little art project. It’s nice to have given it a bit of polish and released it into the world, rather than it gathering dust on my hard drive.
See the project: www.chrismytton.com/art.
Check out the code: chrismytton/art on GitHub.