# Typescript高级操作

tags: TypeScript Created time: December 24, 2022 8:42 PM emoji: <https://www.typescriptlang.org/favicon-32x32.png?v=8944a05a8b601855de116c8a56d3b3ae>

## keyof

keyof操作符的作用是遍历一个对象/接口获取键值。

```jsx
type Point = { x: number; y: number };
type P = keyof Point;
// 相当于
// type P = 'x' | 'y'

const p1:P = 123 // 报错
const p2:P = '888'  // 报错
const p3:P = 'x' // 正确
```

如果使用索引类型

```jsx
type Point = { [name: string]: number };
type P = keyof Point;
// 相当于
// type P = number
```

## typeof

在js中typeof主要作用是判定变量类型，而ts扩展了typeof关键字的作用，使其可以进行“读取类型”操作。

```tsx
const a= 123
const b: typeof a = 123
```

这对于基本类型不是很有用，但与其他类型操作符结合使用，可以使用typeof方便地表示许多模式。例如，让我们从查看预定义的类型ReturnType开始。它接受函数类型并生成其返回类型：

```tsx
function fn():string{
    return '123'
}

type fnType = typeof fn;  // type fnType = () => string
let a: ReturnType<fnType>;
a = '123'  // string
```

为了避免混乱，ts限制typeof关键字后面只能跟标识符(变量名、函数名等)或对象属性。

```tsx
// 错误
function fn():string{
    return '123'
}
let a:typeof fn() = '123'

// 正确
let o = {
    a: '123'
}
let a: typeof o.a = '123'
```

## **索引访问类型(Indexed Access Types)**

ts可以通过索引访问类型。可以通过`type`、`interface`或`对象`来设置类型。

```tsx
// 对象
let o = {
    a: '123'
}
let a: typeof o['a'] = '123'

// 接口
interface O1 {
    a: string
}
type Oa = O1['a']

// 类型别名
type O2 = {
    a: string
}
type Ob = O2['a']
```

甚至你还可以使用联合类型。

```tsx
type A = {
    a: string,
    b:number
}
type B = A['a' | 'b'] // string | number
type C = A[keyof A] // string | number
```

## Mapped Types

有些时候你可能要重复定义某些类型，如果你不想编写重复的代码，可以使用映射类型。

```tsx
type A<Type> = {
    [Property in keyof Type]: boolean;
};

type = {
    a: string;
    b: number;
};
 
type O = A<FeatureFlags>; // {a:boolean;b:boolean}
```

这里使用到了泛型， `[Property in keyof Type]` 会迭代`FeatureFlags` 所有的key，你也可以让所有的key定义为原来的类型。

```tsx
type A<Type> = {
    [Property in keyof Type]: Type[Property];
};
```

注意：在映射类型中不能定义属性和方法。

```tsx
type A<Type> = {
    [Property in keyof Type]: Type[Property];
		a: string; // 错误
		fn:()=>void; // 错误
};
```

## 条件类型

很多时候我们需要根据输入值的类型来判断输出值的类型，这个时候就可以使用条件类型。

条件类型的写法有点类似于 JavaScript 中的条件表达式（`condition ? trueExpression : falseExpression` ）：

```tsx
SomeType extends OtherType ? TrueType : FalseType;
```

例如：

```tsx
type A = number;
type B = A extends number ? number : string
const b:B = 1
```

条件类型配合泛型时就很有用了，例如我们需要实现一个函数，如果传入的参数是字符串则返回`string`，如果参数是数字类型则返回`number`，如果使用泛型+条件类型就可以这样写：

```tsx
function fn<O extends string | number>(arg: O):O extends string ? 'string' : 'number'{
    if(typeof arg === 'string'){
        return 'string' as O extends string ? 'string' : 'number'
    }else {
        return 'number' as any;
    }
}
```

可以将条件类型提取出来：

```tsx
type FnReturnType<O extends string | number> = O extends string ? 'string' : 'number'

function fn<O extends string | number>(arg: O): FnReturnType<O> {
    if(typeof arg === 'string'){
        return 'string' as FnReturnType<O>;
    }else {
        return 'number' as FnReturnType<O>;
    }
}

fn<string>('1')
```

## infer

`infer`需要搭配条件类型来使用，`**infer`的作用是从正在比较的类型中推断类型，然后在 `true`  分支里引用该推断结果。\*\*

我们用下面这个例子来描述`infer`的作用，在下面这个例子中，我们实现`ArrayItemType`类型来获取数组元素的类型，如果`T`是一个数组则返回其元素的类型，否则返回`null`，我们可以这样写：

```tsx
type ArrayItemType<T> = T extends any[] ?  T[number] : null;

const arr = [1];
const a: ArrayItemType<typeof arr> = 0;
```

这里运用了索引访问类型，如果使用infer，代码还能更简洁。

```tsx
type ArrayItemType<T> = T extends Array<infer K> ?  K : null;

const arr = [1];
const a: ArrayItemType<typeof arr> = 0;
```

注意，infer推断的类型只能在条件类型的true分支中使用，不能在false分支使用，例如下面的代码：

```tsx
// 正确的
type ArrayItemType<T> = T extends Array<infer K> ?  null : K;

// 错误的，K不能在false分支使用
type ArrayItemType<T> = T extends Array<infer K> ?  null : K;
```

## 类型谓词

TypeScript的类型谓词是一个检查类型的函数，用于检查一个输入值是否为某个特定类型。它的语法如下：

```tsx
function isString(type: unknown): type is string {
    return typeof type === 'string'
}
```

类型谓词的作用在于它能够检查一个值是否为某个特定类型，并且如果检查成功，返回true，否则返回false。它可以用于类型检查，以确保代码的正确性。

```tsx
function fn(x: unknown) {
    if (isString(x)  /*判断x是否是string类型*/ ) {
        x.toUpperCase();
    }
}
```

## 非空断言

Typescript还有个很实用的操作符——非空断言，它想到于告诉Typescript这个变量肯定不会是undefined或null。

它的用法是变量后面跟个`!`符号。

例如：

```tsx
let a: string|null;
let b:string = a // error，因为a可能null
let c:string = a! // ok
```

也可以用在对象的属性或方法上，就像可选链一样：

```tsx
a!.b // a不为null或undefined
```

例如在vue的$refs中，有时我们明确知道`ref`引用了某个dom节点，例如`this.$refs.linkRef`，但是typescript会认为`this.$refs.linkRef` 有可能为undefined，这个时候就可以用非空断言，如`this.$refs!.linkRef` ，当然另一种写法是用可选链，因为在某些情况下（例如页面跳转）引用的dom节点确实会为空。

## 确定赋值断言

`TypeScript` 的确定赋值断言，允许在实例属性和变量声明后面放置一个 `!` 号，从而告诉 `TypeScript` 该属性会被明确地赋值。

```tsx
let name!: string;
```

举个例子：

```tsx
let a:string;

fn()

a.toLocaleLowerCase()

function fn(){
    a = ''
}
```

在这个例子中，我们已经知道变量a已经被赋值，但是typescript没有检测到，因此我们需要用确定赋值断言明确地告诉

## 实用类型

typescript提供一些全局范围可用的类型，可以简化我们的一些操作。

<https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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://1425816423.gitbook.io/my-knowledge-base/qian-duan-ji-shu/typescript/typescript-gao-ji-cao-zuo.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.
