|
| 1 | +# Null is Bad |
| 2 | +JavaScript (and by extension TypeScript) has two bottom types : `null` and `undefined`. They are *intented* to mean different things: |
| 3 | + |
| 4 | +* Something hasn't been initialized : `undefined` |
| 5 | +* Something is current unavailable: `null` |
| 6 | + |
| 7 | +Most other languages only have one (commonly called `null`). Since by default JavaScript will evaluate an uninitialized variable / parameter / property to `undefined` (you don't get a choice) we recommend you just use that for your own *unavailable* status and don't bother with `null`. |
| 8 | + |
| 9 | +## Real world discussions |
| 10 | +TypeScript team doesn't use `null` : [TypeScript coding guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined) and it hasn't caused any propblems. Douglas Crockford thinks [`null` is a bad idea](https://www.youtube.com/watch?v=PSGEjv3Tqo0&feature=youtu.be&t=9m21s) and we should all just use `undefined` |
| 11 | + |
| 12 | +## Dealing with `null` style code bases |
| 13 | +If your code base interacts with other APIs that might give you a `null` you check with `== undefined` (instead of `===`). Using this is safe even for other potentially *falsy* values. |
| 14 | +```ts |
| 15 | +/// Image you are doing `foo == undefined` where foo can be one of: |
| 16 | +console.log(undefined == undefined); // true |
| 17 | +console.log(null == undefined); // true |
| 18 | +console.log(0 == undefined); // false |
| 19 | +console.log('' == undefined); // false |
| 20 | +console.log(false == undefined); // false |
| 21 | +``` |
| 22 | + |
| 23 | +## Additional tips |
| 24 | + |
| 25 | +### Limit explict use of `undefined` |
| 26 | +Also because TypeScript gives you the opportunity to *document* your structures seperately from values instead of stuff like: |
| 27 | +```ts |
| 28 | +function foo(){ |
| 29 | + // if Something |
| 30 | + return {a:1,b:2}; |
| 31 | + // else |
| 32 | + return {a:1,b:undefined}; |
| 33 | +} |
| 34 | +``` |
| 35 | +you should use a type annotation: |
| 36 | +```ts |
| 37 | +function foo():{a:number,b?:number}{ |
| 38 | + // if Something |
| 39 | + return {a:1,b:2}; |
| 40 | + // else |
| 41 | + return {a:1}; |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Node style callbacks |
| 46 | +Node style callback function (e.g. `(err,somethingElse)=>{ /* something */ }`) are generally called with `err` set to `null` if there isn't an error. You generally just use a truthy check for this anyways: |
| 47 | + |
| 48 | +```ts |
| 49 | +fs.readFile('someFile', 'utf8', (err,data) => { |
| 50 | + if (err) { |
| 51 | + // do something |
| 52 | + } |
| 53 | + // no error |
| 54 | +}); |
| 55 | +``` |
| 56 | +When creating your own APIs its *okay* to use `null` in this case for consistency. In all sincerity for your own APIs you should look at promises, in that case you actually don't need to bother with absent error values (you handle them with `.then` vs. `.catch`). |
| 57 | + |
| 58 | +### Don't use `undefined` as a means of denoting *validity* |
| 59 | + |
| 60 | +For example an awful function like this: |
| 61 | + |
| 62 | +```ts |
| 63 | +function toInt(str:string) { |
| 64 | + return str ? parseInt(str) : undefined; |
| 65 | +} |
| 66 | +``` |
| 67 | +can be much better written like this: |
| 68 | +```ts |
| 69 | +function toInt(str: string): { valid: boolean, int?: number } { |
| 70 | + const int = parseInt(str); |
| 71 | + if (isNaN(int)) { |
| 72 | + return { valid: false }; |
| 73 | + } |
| 74 | + else { |
| 75 | + return { valid: true, int }; |
| 76 | + } |
| 77 | +} |
| 78 | +``` |
0 commit comments