Idiomatic CoffeeScript is a set of best practices in CoffeeScript programming. This project is inspired in Idiomatic JavaScript by @rwaldron.
This also serve as a programming style guide, which is useful for team development, so everyone can code in the same fashion.
As said on the Idiomatic JavaScript document: "All code in any code-base should look like a single person typed it, no matter how many people contributed", and that's absolutely true. So this guide's purpose is to apply that concept in CoffeeScript as well.
Important: In order to make this guide simple to follow it doesn't offer options and paths as the Idiomatic JavaScript does, if you want to create new rules or change stuff you can fork this guide and customize it to fit your tools and style.
Link this document mentioning it as the style guide for your project, so everybody know how you code. The styling guide should be the first thing you define in your project, after it is done everything must be based on it.
You can also link directly to the strict version of this document which introduces a more strict style of writing.
Always remember: CoffeeScript is just JavaScript. It is not an entirely new language, it is just a cool tool you can use to improve productivity and the readability of your code. You should use it wisely otherwise you could end up generating terrible JS. This guide will help you get an idea of the path you need to follow to get the most out of CoffeeScript.
Reading this article is strongly recommended to get an idea on what CoffeeScript is. Also, this guide assumes that you already know you to use CoffeeScript. If you are a rookie please skip to this section. Important stuff clarified we can proceed with the guide.
It is important to remember why you are writing CoffeeScript in the first place. Your focus is on readability, better readability than JavaScript while writing less code. So this guide's job is to put together some tips that will help you achieve that.
- Use 2 spaces to indent, most editors enable you to convert tabs into spaces;
- Don't leave trailing spaces. Again, you editor can help with that matter;
- Use one blank line to separate function declarations;
- Use one blank line to separate properties within a
Class
; - Don't leave blank lines at the end of the code in each file;
- Don't use blank lines to separate lines within the same function.
- Always use double quotes
"
in strings, everywhere; - Break long strings, CoffeeScript supports multi-line strings;
- Use CoffeeScript's string interpolation
"#{data}"
instead of concatenation with+
.
- Use single line
#
comments most of the time; - Only use multi line comments
###
on the header which contains information about the file. - Use one space after the single
#
symbol before starting the comment sentence, this improves readability. - When using external automatic documentation software you may override this rule if necessary.
Example:
```coffeescript
add = (num1, num2) ->
return num1 + num2
result = add(1, 2)
# Breaking line because string has more than 80 characters
# Important: Check if your spacing is right before breaking
string = "We performed a sum between 1 and 2 and the result
is #{result}"
```
- Variables should be written in lowerCamelCase;
- Constants should be written in UPPERCASE with
_
if necessary; - Class names should be written in UpperCamelCase;
- Make scope clear, as CoffeeScript always assume the keyword
var
when a new variable is presented to avoid accidental global scoping you must be sure that your variable is declared in the right spot. Remember that CoffeeScript also always automatically declare your variable before using it if it does not found an existing variable with the same name. - You should avoid unnecessary global variables but if you want them you should attach your variable to the
window
object in the browser or declare it on the global scope on other JS environments.
Problem:
# This is a scope problem, the logging of the foo variable
# will be undefined because CoffeeScript correctly defined it
do ->
foo = "bar"
console.log(foo)
Solution:
# To solve the problem we must declare foo
# foo must assume a default value so CoffeeScript will
# declare it for us
foo = ""
do ->
foo = "bar"
# Now it's logging "bar"!
console.log(foo)
- Always explicitly call
return
unless it's void, do not use CoffeeScript's implicit return; - When you have to pass a variable number of arguments to a function, use splats;
- Instead of executing simple functions in loops use object or array comprehensions;
- Don't use object or array comprehensions for complex stuff, it makes it hard to read;
- Make use of closures created with
do
when building your modules; - Use CoffeeScript's boolean flexibility to improve readability when suitable;
- Use CoffeeScript's existential operator
?
; - Don't use commas when writing long literal array and object notations, instead, add line breaks at each node.
- CoffeeScript gives you the option to omit parenthesis on certain situations, it's OK to do it everywhere you can but function calls, you should call functions with arguments surrounded by parenthesis. Always.
Example:
# Using closure, you can add arguments here as well
do ->
# Array declaration without commas
pilots = [
"Senna"
"Alonso"
"Schumacher"
"Vettel"
"Hamilton"
"Barichello"
]
# Sample podium function, showing how you can use splats,
# string line breaks, string interpolation and implicit return
podium = (first, second, third, rest...) ->
"Gold Trophy: #{first}, Silver trophy: #{silver}, Bronze trophy:
#{bronze}. Contenders: #{rest.join(",")}"
# Calling a function with splats
podium pilots...
# A more friendly approach to boolean values.
# No need to use splats here, I just use to reuse the elements
# I already had.
didSennaWin = (first, pilots...) ->
if first is "Senna"
return yes
else
return no
- Use CoffeeScript classes when applicable
- Classes create closures
- Classes are inheritable
- Classes are reusable
Example:
###
Example class Car
Author: Matheus R. Kautzmann
###
class Car
# Instance properties
# Default brand
brand: "Other"
# Car start at distance 0
distance: 0
# Default year
year: 2013
# Air Conditioner is off by default
airConditioner: off
# Color is white by default
color: "White"
# Class properties
@possibleBrands: [
"Audi"
"Mercedes"
"BMW"
"Ferrari"
"Lamborghini"
"Volvo"
"Fiat"
"GMC"
"Toyota"
"Nissan"
"Renault"
"Other"
]
# Class methods
@currentModelYear: ->
actualDate = new Date()
actualYear = actualDate.getFullYear()
# Explicit return statement improves readability
return (actualYear + 1)
# Constructor
constructor: (brand, model, year, color) ->
@brand = brand if brand in Car.possibleBrands
@model = model
@paint(color)
@year = year if year <= Car.currentModelYear()
# Instance methods
move: (distance) ->
if distance? then @distance += distance
paint: (color) ->
@color = color if color?
toggleAirConditioner: ->
if @airConditioner
@airConditioner = off
else
@airConditioner = on
- Since version 1.9.0 CoffeeScript supports ES6 generators;
- Use generators to create custom functions that yields series of values;
- Remember that CoffeeScript is just JS, so your target platform must support ES6 generators feature;
- Use it when it makes sense, like when you are writing a function that you would later call iterating over a specific series of values.
Example:
# Creating factorial generator to later iterate on
factorial = ->
num = 0
result = 0
loop
result = num * result
if num < 2 then result = 1
if num is 2 then result = 2
num++
yield result
return
# Iterating over factorial generator
it = new factorial()
# Get factorials from 0 to 10
console.log(it.next()) for i in [0..10]
Sometimes it's hard to follow the changes in code between CoffeeScript and compiled JS. To help address this issue Source Maps were introduced in CoffeeScript in version 1.8.0.
Source Maps tells browsers that the JS file was generated by CoffeeScript, so the browser can map JS runtime to the original file written in CoffeeScript allow debugging directly with the CoffeeScript file! This helps a lot when checking your code for bugs because you just need to check your .coffee files.
To use source maps you must enable the feature in your CoffeeScript compiler, the CoffeeScript module for Node.JS has the option available as an argument, you can call the command in your CLI as shown below.
coffee --compile --map myfile.coffee
- Always lint your CoffeeScript code, you can use CoffeeLint;
- Do not lint the JS generated by CoffeeScript. Instead, lint your CoffeeScript directly as said above;
- You should write automated tests for your CoffeeScript app;
- Write your tests in CoffeeScript, Mocha is a good option;
- If you need a browser environment, PhantomJS with Mocha is a nice option.
- Always compile your CoffeeScript to JS, never use CoffeeScript code directly. Unless your are writing a CoffeeScript web transpiler or something like that;
- You can use the CoffeeScript node module itself to compile .coffee files.
- Use UgilifyJS or YUI Compressor to do this.
- Use Grunt or Gulp to setup your build process;
- Do everything important in this process: lint, test, compile to JS, minify JS, etc;
- It is strongly recommended the use of a continuous integration server, Travis CI is an excellent option if you use GitHub.
Learning more about CoffeeScript is always nice. Here are listed some great resources to get started with CoffeeScript or, if you are already experienced with it, you can learn some good tips and tricks you can use in your code.
This guide is being written by Matheus Kautzmann and it is always changing. This repo is always open to contributors, so if you disagree of something present in this document or want to add an important thing to it feel free to make a pull request or open an issue.
This project is under MIT License.
The MIT License (MIT) Copyright (c) 2015 Matheus Kautzmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.