# Types in TypeScript

## Object Types

在函数声明时，可以指定传入 object 的类型

```typescript
function foo (obj: { x: number, y: number }){
  ...
}
```

指定其中某些参数是 `optional` 的

```typescript
function foo (obj: { x: number, y?: number }){
  ...
}
// both ok
foo({x: 1, y: 1})
foo({x: 1})
```

不过，如果某一参数是 `optional` 的，那么就应该在函数体内部对它进行判空

```typescript
function foo (obj: { x: number, y?: number }){
  if(obj.y !== undefined){
    ...
  }
}
```

## Union Types

可以由多个现有的 type 构建新的 type

```typescript
function foo(id: number | string) {...}

foo(1)		// allowed
foo(true) // not allowed
```

foo接受的 id 就是一个 `union type` ，他可以是一个 number 或者 string

与上面的 `optional` 类似，如果在函数中定义了一个参数为 `unino type`，那么在函数体内可能需要 `narrowing`

```typescript
function foo(id: number | string) {
  // not allowed，因为 id 可能是 number，number 没有 toUpperCase() 属性
  id.toUpperCase()
  // narrowing，allowed
  if(typeof id === 'string')
    id.toUpperCase()
}
```

## type 关键字

类似于其他语言的 `typedefine`，可以将上述的 Object Types、Union Types 定义成一个新的”类型“

```typescript
type Point = {
  x: number,
  y: number
}
type ID = number | string

// useage
function foo(point: Point)
function foo(id: ID)
```

> 注意的是，type 定义的新类型其实就是一个“别名” **(type aliases)**
>
> 如 type NewString = string，本质上 NewString 就是 string 类型，它们不是两个不同的类型

## interface 关键字

`interfaces` 关键字是另一种给 Object Types 定义”类型“的方式

```typescript
interface Point = {
  x: number,
  y: number
}
```

## type aliases 和 interface 的区别

两者在大部分情况下没有区别，可以随意选择使用。

> 在多数情况下，可以按照自己的偏好选择 type 或者 interface，不过有一个不错的策略是：
>
> **尽量使用 interface，除非必须使用 type 的时候**

关键的区别就是：`interfaces`**是可以以增加新的属性的**

```typescript
interface Animal {
  name: string
}
// interface 创建后可以继续添加属性
interface Animal {
  age: number
}

const cat: Animal
cat.name = 'xxx'
cat.age = 1
```

```typescript
type Animal = {
  name: string
}
// type 创建后就不能修改
type Aniaml = {
  age: number
}
```

### interface 增加属性

```typescript
interface Animal {
  name: string
}
interface Bear extends Animal {
  honey: boolean
}

const bear = getBear() 
bear.name
bear.honey
```

### type 增加属性

```typescript
type Animal = {
  name: string
}
type Bear = Animal & { 
  honey: boolean 
}

const bear = getBear();
bear.name;
bear.honey;
```

## type 断言

有时候，你能确定一个值的类型，那么就可以使用断言，例如你确定 id 为 'main\_canvas' 的 DOM 元素一定是 HTMLCanvasElement 类型

```typescript
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement
```

另一种用`<>`的写法

```typescript
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas")
```

> 类型断言在编译后会被移除，所以不必担心会出现 runtime 时问题

断言只能**缩小**或者**扩大**某一个类型，如下面的例子是不合法的

```typescript
const foo = 'bar' as number		// not allowed
```

但是上述这一个限制有时候也有点太妨碍了，可以用下面的方法来解决

```typescript
const foo = (bar as any) as T		// 先转为 any 再转为想要的类型
```

## 字面量 type

在 Typescript 中，下面两种写法是等价的

```typescript
const foo = 'bar'
const foo: 'bar' = 'bar'

foo = 'foorbar'		// not allowed
```

不过上面的第二种写法没有太大意义，但是利用`union type`，可以限制某一变量的取值范围

```typescript
const foo: 'foo' | 'bar' = 'bar'
foo = 'foo'			// ok
foo = 'foobar'	// not ok
```

> union type 不仅仅可以组合相同类型的 type，下面的写法也是可以的

```typescript
type Foo = 'bar' | number

let foo: Foo = 1		// ok
foo = 'bar'					// ok
foo = 'foobar'			// not ok
```

## null & undefined

TypeScript 和 JavaScript 一样，也有 null 和 undefined。但是它们的行为取决于 `strictNullChecks` flag 是否开启

### strictNullChecks off

null 和 undefined 仍然可以被正常访问，也能当做属性赋值给任意值。这和 C# Java 类似，并不做 null checks。

### strictNullChecks on

> null undefined 是大多数 bug 的来源，因此推荐打开 `strictNullChecks`

当 `strictNullChecks` 打开时，需要在访问可能为空的值时，提前判空

### ! 断言

如果能 100% 保证变量不会为空，可以使用 `!` 断言，以省去判空逻辑

```typescript
function foo(x: number){
  console.log(x!.toFixed(2))
}
```

## Enums 枚举类型

Enums 分为数字型和字符串型

**数字型**

```typescript
// 如果不指定，默认从 0 开始
enum Direction {
  Up = 1, Down, Left, Right
}
```

**字符串型**

```typescript
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}
```

> Enums 并不是 TypeScript 对 JavaScript 在\*\*“类型系统层面”\*\*上的扩展，因为 JavaScript 本身并没有枚举类型。如上面的数字型 enum 在 tsc 编译后的 js 文件是这样的：

```javascript
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 1] = "Up";
    Direction[Direction["Down"] = 2] = "Down";
    Direction[Direction["Left"] = 3] = "Left";
    Direction[Direction["Right"] = 4] = "Right";
})(Direction || (Direction = {}));
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hong97.ltd/typescript/types.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
