# 앰비언트 선언도 선언병합이 된다.

## 앰비언트 선언 (declare)

> 타입 정보만 제공하는 선언

* 실제 구현이 존재하지 않는 코드의 타입을 정의 (코드 구현부 없음)
* 외부 라이브러리의 타입을 알리는데 사용됨
* 즉, 외부 라이브러리를 가져다 쓰는데 자바스크립트로만 된 코드의 경우 직접 타이핑 해야할  때 사용
* **`declare`** 예약어를 사용

```typescript
declare namespace NS {
  const v: string; // 변수에도 타입만 있고 값을 대입하지 않음
}
declare enum Enum {
  ADMIN = 1
}
declare function func(param: number): string;
declare const variable: number;
declare class C {
  constructor(p1: string, p2: string);
}

// 외부 파일에 실제 값이 존재한다고 믿기 때문에 아래처럼 값으로 사용 가능
// 단 값이 없으면 에러 발생
new C(func(variable), NS.v); 
```

* **`declare`**&#xB85C; 앰비언트 선언할 때는 반드시 해당 값이 실제로 존재함을 확인해야 한다.

{% hint style="info" %}
인터페이스와 타입 별칭도 declare로 선언 가능, But, declare로 선언하지 않아도 동일하게 작동하므로 굳이

```typescript
declare interface Int {}
declare type T = number;
```

{% endhint %}

* 타입스크립트에서 선언할 수 있는 타입으로는 **`네임스페이스`**, **`클래스`**, **`enum`**, **`인터페이스`**, **`타입 별칭`**, **`함수`**, **`변수`**

### 이들은 선언할 때 네임스페이스나 타입 또는 값으로 사용될 수 있다.

| 유형     | 네임스페이스 | 타입 | 값  |
| ------ | ------ | -- | -- |
| 네임스페이스 | ⭕️     |    | ⭕️ |
| 클래스    |        | ⭕️ | ⭕️ |
| enum   |        | ⭕️ | ⭕️ |
| 인터페이스  |        | ⭕️ |    |
| 타입 별칭  |        | ⭕️ |    |
| 함수     |        |    | ⭕️ |
| 변수     |        |    | ⭕️ |

### 같은 이름의 다른 선언과 병합 가능 여부

| 병합 가능 여부 | 네임스페이스 | 클래스 | enum | 인터페이스 | 타입 별칭 | 함수 | 변수 |
| -------- | ------ | --- | ---- | ----- | ----- | -- | -- |
| 네임스페이스   | ⭕️     | ⭕️  | ⭕️   | ⭕️    | ⭕️    | ⭕️ | ⭕️ |
| 클래스      | ⭕️     | ❌   | ❌    | ⭕️    | ❌     | ⭕️ | ❌  |
| enum     | ⭕️     | ❌   | ⭕️   | ❌     | ❌     | ❌  | ❌  |
| 인터페이스    | ⭕️     | ⭕️  | ❌    | ⭕️    | ❌     | ⭕️ | ⭕️ |
| 타입 별칭    | ⭕️     | ❌   | ❌    | ❌     | ❌     | ⭕️ | ⭕️ |
| 함수       | ⭕️     | ⭕️  | ❌    | ⭕️    | ⭕️    | ⭕️ | ❌  |
| 변수       | ⭕️     | ❌   | ❌    | ⭕️    | ⭕️    | ❌  | ❌  |

{% hint style="info" %}
이 표를 모두 외우는건 쉽지 않기 떄문에 **인터페이스**, **네임스페이스 병합**이나 **함수 오버로딩**과 같이 널리 알려진 경우를 **제외하고는 웬만하면 같은 이름으로 여러번 선언하지 않는 것이 좋다.**
{% endhint %}

### 선언 병합을 활용하면 좋은 예

#### 클래스의 new 연산자를  사용하지 않고 생성하게  만드는 코드

* declare로 앰비언트 선언한 타입도 병합됨
* 앰비언트 선언한 타입과 그렇지 않은 타입끼리도 병합된다.

```typescript
declare class A { // 앰비언트 선언
  constructor(name: string);
}
function A(name: string) {
  return new A(name);
}

new A('zerocho');
A('zerocho');
```

**함수에 속성이 별도로 있다는걸 알리고 싶은 경우**

* JS에서 함수도 객체이므로 함수에 속성을 추가할 수 있음
* 함수와 네임스페이스가 병합될 수 있으므로 아래 코드에서 에러가 발생하지 않음
* **함수에 속성이 별도로 있다는걸 알리고 싶은 경우** 함수와 동일한 이름의 namespac를 추가하면 된다.

```typescript
function Ex() {return 'hello';}
namespace Ex {
  export const a = 'world';
  export type B = number;
}
Ex(); // hello
Ex.a; // world
const b: Ex.B = 123;
```
