-
Notifications
You must be signed in to change notification settings - Fork 0
Coding Style
The examples for the usage of FUDGE import its modules with names prefixed by the florin character. This is not mandatory but just a matter of taste. Some people prefer to use a simple f, you can choose whatever you like and also use names with more characters. However, if you enjoy using ƒ, here is how you can add a keyboard shortcut in VSCode for it: https://github.com/JirkaDellOro/FUDGE/issues/39
The developers code must be self-explanatory. This requires the strict use of naming conventions. Use names, that clearly explain the activity or information addressed and don't be greedy with letters. Short names are allowed only in very small scopes or when their meaning is clear by convention, such as y
for a vertical position.
The names of variables and functions must start lowercase and follow the camelCase notation, with uppercase letters indicating the start of a new part in a compound name such as animalLion
. The names of variables describe an information or an object. Names of functions and methods strictly describe activities e.g. calculateHorizontalPosition(...)
or questions e.g. isHit()
Name formal parameters in a functions signature like variables, but prefix them with an underscore: (_on: boolean)
Names of classes, interfaces and namespaces or modules start with an uppercase Letter and then follow the camelCase notation (PascalCase). The name describes exactly one object of that type, not an activity e.g. ObjectManager
.
The names of enumerations and their elements are written all uppercase, with underscores separating parts of the name e.g. EVENT_TYPES.EXIT_FRAME
Functions or static methods that create, calculate and return a result independent of the state of the application or any objects are considered "calculated enumerations". Thus, the style for regular Enums apply with the addition of a signature, e.g. ROTATION_X(_angleInDegrees: number): Matrix4x4
Static methods of a class, that do not alter any state, but solely create and return a value or object for further processing, may be named in such a way as to describe the returned value and be written all uppercase. Like enumerations or constants, they represent values, but are calculating these at runtime. For example Vector3.DIFFERENCE(_a: Vector3, _b: Vector3): Vector3
returns a new Vector calculated by subtracting vector _b
from vector _a
.
For comparison, the non-static method Vector3.subtract(_subtrahend: Vector3): void
alters the vector it is called upon by subtracting the '_subtrahend' from it while not returning a value.
Bad example from the DOM-API: getElementById(...)
vs getElementsByTagName(...)
. Only the little s
in the middle indicates that one returns a collection, not a single element. Better: getElementCollectionByTagName(...)
. However, in getElements(...)
, the s
is clearly visible.
For example, state
may have different meaning depending on the context. Machine.state
indicates something different than Address.state
. However, it is redundant to write Machine.stateTheMachineOperatesIn
or Address.stateAsThePoliticalEntity
, since the context is provided already by the namespaces. Use this instead of implementing redundancies.
Some prefixes may be helpful for finding names for variables, use is encouraged. Following is a list of prefixes that should be used.
Prefix | Example | Meaning |
---|---|---|
n |
nObjects |
an amount |
i |
iObject |
an index |
x , y , z
|
xPos |
a direction or dimension |
min , max
|
maxHeight |
boundaries |
pos , rot
|
rotCube |
a position or rotation |
cmp |
cmpMaterial |
a component |
shd |
shdTexture |
a shader |
mtr |
mtrSoil |
a material |
clr |
clrRed |
a color |
img |
imgHouse |
an image |
mtt |
mttCamera |
a mutator |
srl |
srlNode |
a serialization |
mtx |
mtxLocal |
a matrix |
vct |
vctPosition |
a vector |
id |
idResource |
an identifier |
hnd |
hndEvent |
a handler |
Find new prefixes by determining the kind of information described (not necessarily the data type), stripping away the vowel and taking the first three letters.
Always use visibility modifiers!
Though public
is the default, also mark properties as public
if they are
For protected properties use the Florin (ƒ) as a prefix. This way, you can create accessors with the same name but without the Florin.
Wherever possible, use the javascript notation (#) for private instance fields, don't use the private keyword. This way, you can create accessors with the same name but without the #. This notation is not fully supported yet by Firefox and TypeScript doesn't manage the use on static fields (as of 2/21). Also, the mutator will not collect these properties! Where necessary, use the private
keyword and the Florin (ƒ) as a prefix, just as for protected properties.
See https://github.com/JirkaDellOro/FUDGE/wiki/Coding-Style#formal-parameters
Use comments sparsely! If you feel that some code needs commenting rethink it and the naming of its components. Remember that you need to maintain comments just as you need to maintain code. Otherwise comments are not only useless but obstructive. Comments are allowed in the following cases
Regions in code may circle around a specific aspect and can therefore be separated by comments. However, if that aspect justifies the creation of a class concerning it, this should be preferred. To define a region use the following syntax in order to use the folding feature of editors
// #region NameOfTheRegion (and additional comment if necessary)
// #endregion
Mark unfinished business with a TODO
comment. The IDE (-Plugins) can find, list and highlight those comments
This is required. TS-Doc comments must be inserted before each namespace class, interface, enum and public method and kept up to date. Use /**
Comments about licensing or links to sources used etc.
A developer using a class should not be forced to learn its internal structure first, but must be enabled to intuitively work with it. This rule has some consequences on the design of such a class.
- mostly, all attributes should be private or protected.
- don't implement accessors just to alter or retrieve those attributes. If necessary, make them public.
- however, if an attribute is not entirely intuitive like
color
, it should not be revealed, since the valid manipulation would require knowledge of internals. Instead, provide functions that describe a desired effect, so that the user can write for exampleMotor.start()
instead ofMotor.running = true
- Using a accessors must not have sideffects other then what is needed to acquire or store the appropriate information. A user will not expect anything other than setting or retrieving a value. Use a method instead implies that there may be more complex things happening in the background and sideeffects altering the objects state. Compare
object.x = 10
toobject.setHorizontalPosition(10)
A function should not consist of more than 20 lines of code. If possible, split it up into smaller functions each of which has an explanatory name. This way, the calling function consists of multiple calls that are easy to read and interpret, and the concerns are distributed to smaller functions with the same qualities. Also, watch out for the size of classes, beware of monsters! Keep the number of attributes low
One function/method should care only about one concern and do this well
A function should not indent more than two levels. Use return statements not only at the end, but so called "early outs" and throw exceptions to keep indentation level low.
Order functions and methods in such a way, that the call sits above the called function in code. Reading from top to bottom, the code displays the hierarchy of calls making it possible to understand the overall structure first before diving into the details.
Strictly use explicit typing wherever possible. The type any
is prohibited.
Always end statements with semicolon.
Literal strings should be enclosed in double quotiation marks e.g. "Hello World!"
Are simply disallowed. Never use a literal value in a function call when its meaning is not extremely obvious (e.g. Math.pow(x, 5)
to retrieve x to the power of 5). In all other cases, define a variable with a explanatory name and assign the literal value to it. This way, there is a value and a meaning to it, and its value can be changed in a single place.
Use one file per class. As an exception, tiny subclasses may be stored with the superclass. Interfaces may be stored with the class they are strongly connected to. Use PascalCase for filenames, exactly the same name as the classes.
https://github.com/basarat/typescript-book/blob/master/docs/styleguide/styleguide.md
https://github.com/Platypi/style_typescript
https://github.com/excelmicro/typescript