# Mold

## TypeScript

Mold的出現主要是因為JavaScript沒有Interface、Type等Struct的功能，這在運行過程時容易遇到非預期的錯誤。

TypeScript是一個解決方案，以下是使用時呈現的方式：

```javascript
// Basic function
function sum(v1: number, v2: number): number {
    return v1 + v2
}

// Interface based
interface Person {
    firstName: string
    lastName: string
}

function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName
}
```

{% hint style="info" %}
TypeScript的限制在編譯後就失去了功能，意味著今天在與客戶端交流時仍難以避免資料型態的意外。
{% endhint %}

## How To Use

Mold不能被外部所引用，最直接的使用方法是宣告在Tool的Request內。

```javascript
const tools = {
    demo: {
        request: ['string', 'number'],
        handler: self => self.success()
    }
}
```

在執行時能便會附加檢查功能：

```javascript
packhouse.tool('group/demo').action('123', '456', error => {
    console.log(error.message) // Parameter 1 not a number(456).
})
```

### Expression

Mold可以藉由`|`符號加入參數，協助開發者應付特殊狀況。

```javascript
const tools = {
    demo: {
        request: ['number|min:50'],
        handler: self => self.success()
    }
}
```

### Casting

能夠使用`self.casting`在[Handler](https://packhouse-doc.metalsheep.com/the-instance/tool#handler)中做轉換。

```javascript
const tools = {
    demo: {
        handler(self, value) {
            try {
                let newValue = self.casting('number|min:50', value)
                self.success(newValue)
            } catch(e) {
                self.error(e)
            }
        }
    }
}
```

### Null

在Request中宣告`null`可以忽略驗證欄位。

```javascript
const tools = {
    demo: {
        request: [null, 'number'],
        handler: self => self.success()
    }
}
```

### Optional

在名稱後加入`?`符號可以使該參數為`null`或`undefined`的時候忽略驗證。

```javascript
const tools = {
    demo: {
        request: ['number?|min:50'],
        handler: self => self.success()
    }
}
```

## Custom Define Mold

Mold可以同時具有檢查與轉換的功能，呈現的方式如下：

{% hint style="info" %}
Mold不允許非同步運算。
{% endhint %}

```javascript
const molds = {
    int(value) {
        if (typeof value === 'number') {
            return Math.floor(value)
        } else {
            throw new Error('Value not a number.')
        }
    }
}
```

第二個參數為`context`，能夠做更進階的應用：

```javascript
const molds = {
    int(value, { index, utils, extras }) {
        if (extras.positive === true && value >= 0) {
            throw new Error(`Params ${index} must positive.`)
        }
        if (extras.min && Number(extras.min) > value) {
            throw new Error(`Params ${index} less than ${extras.min}.`)
        }
        if (typeof value === 'number') {
            return Math.floor(value)
        } else {
            throw new Error(`Params ${index} not a number.`)
        }
    }
}

const tool = {
    demo: {
        request: ['int|min:50|positive'],
        handler: (self, value) => self.success(value)
    }
}
```

## Verify Expression

基於[`utils.verify`](https://packhouse-doc.metalsheep.com/utils#verify)的格式，有更好的結構表達與文件生產。

```javascript
const molds = {
    person: {
        firstName: [true, ['string']],
        lastName: [true, ['string']]
    }
}

const tools = {
    demo: {
        request: ['person'],
        handler: (self, person) => self.success()
    }
}

let person: {
    firstName: 'green',
    lastName: null
}

// ... 省略註冊行為

packhouse.tool('group/demo').action(person, error => {
    // Parameter 0 verification error, Key(lastName) is required.
    console.log(error)
})
```

## Core Molds

不是每個Mold都要重新定義，系統本身提供了許多常用的型態：

#### type

使用[`utils.getType`](https://packhouse-doc.metalsheep.com/utils#gettype)作驗證。

* { string } is - 目標type，一定需要該參數

```javascript
['type|is:string']
```

#### number

參數必須為數字。

* **min** - 數字必須高於指定數字
* **max** - 數字必須低於指定數字
* **int** - 數字轉換成整數

```javascript
['number|min:0|max:50|int']
```

#### boolean

參數必須為`true`或`false`。

#### string

參數必須為字串。

* **is** - 指定字串內容，可以用`,`分割多個對象。

```javascript
['string|is: red, green, blue']
```

#### array

參數必須為陣列。

#### buffer

參數必須為`Buffer`。

#### object

參數必須為`Object`。

#### function

參數必須為`Function`。

#### date

參數必須為`new Date`支援的時間格式。

#### required

參數為必填。

> 如果你這樣寫required?，會無效。

## Create Self Core Mold

開發人員可以自定義系統Mold，這樣就能夠在所有場合使用自定義Mold。

{% hint style="info" %}
我們不建議個別自定義系統Mold，若有需要請參考[Merger](https://packhouse-doc.metalsheep.com/the-instance/merger)章節來管理Mold。
{% endhint %}

{% hint style="info" %}
當系統Mold與Group Mold有相同名稱時，Group Mold優先。
{% endhint %}

```javascript
let packhouse = new Packhouse()
packhouse.addMold('int', value => parseInt(value))

const tools = {
    demo: {
        request: ['int'],
        handler: self => self.success()
    }
}
```
