# TypeScript - 操作文档和条目

> Source: https://strapi.nodejs.cn/cms/typescript/documents-and-entries

🌐 Manipulating documents and entries with a TypeScript-based project

在 Strapi v5 TypeScript 项目中安全地操作文档和条目，使用 `UID` 和 `Data` 命名空间以确保类型安全，同时支持通用和已知实体类型的操作，并提供代码补全功能。

🌐 Safely manipulate documents and entries in Strapi v5 TypeScript projects using the `UID` and `Data` namespaces for type safety, enabling both generic and known entity type operations with code completion.

本指南将探讨在 Strapi v5 应用中操作文档和条目的 [TypeScript](/cms/typescript) 模式，包括如何利用 Strapi 的 `UID` 和 `Data` 命名空间安全地与通用和已知的实体类型进行交互。如果你正在开发基于 TypeScript 的 Strapi 项目，掌握这些方法将帮助你充分利用类型安全和代码自动补全，确保与应用的内容和组件进行稳健、无错误的交互。

🌐 This guide will explore [TypeScript](/cms/typescript) patterns for manipulating documents and entries in a Strapi v5 application, including how to leverage Strapi's `UID` and `Data` namespaces to interact with both generic and known entity types safely. If you're working on a TypeScript-based Strapi project, mastering these approaches will help you take full advantage of type safety and code completion, ensuring robust, error-free interactions with your application’s content and components.

:::prerequisites

- **Strapi 应用：** 一个 Strapi v5 应用。如果你还没有，请按照[文档](/cms/quick-start)开始使用。
- **TypeScript:** 确保在你的 Strapi 项目中已设置 TypeScript。你可以参考 Strapi 的[官方指南](/cms/typescript#getting-started-with-typescript-in-strapi)来配置 TypeScript。
- **生成的类型：** 应用类型已[生成](/cms/typescript/development#generate-typings-for-content-types-schemas)并可访问。

  :::

## 类型导入 {#type-imports}

🌐 Type Imports

`UID` 命名空间包含表示应用中可用资源的字面量联合。

🌐 The `UID` namespace contains literal unions representing the available resources in the application.

```typescript

```

- `UID.ContentType` 代表应用中每个内容类型标识符的联合
- `UID.Component` 表示应用中每个组件标识符的联合
- `UID.Schema` 表示应用中每个模式（内容类型或组件）标识符的联合
- 等等...

---

Strapi 提供了一个 `Data` 命名空间，其中包含多个用于实体表示的内置类型。

🌐 Strapi provides a `Data` namespace containing several built-in types for entity representation.

```typescript

```

- `Data.ContentType` 代表一个 Strapi 文档对象
- `Data.Component` 代表一个 Strapi 组件对象
- `Data.Entity`表示文档或组件

:::tip

实体的类型定义和 UID 均基于为你的应用生成的架构类型。

🌐 Both the entities' type definitions and UIDs are based on the generated schema types for your application.

如果出现不匹配或错误，你可以随时[重新生成类型](/cms/typescript/development#generate-typings-for-content-types-schemas)。

🌐 In case of a mismatch or error, you can always [regenerate the types](/cms/typescript/development#generate-typings-for-content-types-schemas).

:::

## 使用 {#usage}

🌐 Usage

<br />

### 通用实体 {#generic-entities}

🌐 Generic entities

在处理通用数据时，建议使用 `Data` 类型的非参数化形式。

🌐 When dealing with generic data, it is recommended to use non-parametrized forms of the `Data` types.

#### 通用文件 {#generic-documents}

🌐 Generic documents

```typescript
async function save(name: string, document: Data.ContentType) {
  await writeCSV(name, document);
  //                    ^ {
  //                        id: Data.ID;
  //                        documentId: string;
  //                        createdAt?: DateTimeValue;
  //                        updatedAt?: DateTimeValue;
  //                        publishedAt?: DateTimeValue;
  //                        ...
  //                      }
}
```

:::warning

在前面的例子中，`document` 的解析属性是所有内容类型共有的属性。

🌐 In the preceding example, the resolved properties for `document` are those common to every content-type.

必须使用类型保护手动检查其他属性。

🌐 Other properties have to be checked manually using type guards.

```typescript
if ('my_prop' in document) {
  return document.my_prop;
}
```

:::

#### 通用组件 {#generic-components}

🌐 Generic components

```typescript
function renderComponent(parent: Node, component: Data.Component) {
  const elements: Element[] = [];
  const properties = Object.entries(component);

  for (const [name, value] of properties) {
    //        ^        ^
    //        string   any
    const paragraph = document.createElement('p');

    paragraph.textContent = `Key: ${name}, Value: ${value}`;

    elements.push(paragraph);
  }

  parent.append(...elements);
}
```

### 已知实体 {#known-entities}

🌐 Known entities

在操作已知实体时，可以对 `Data` 类型进行参数化，以获得更好的类型安全性和代码补全。

🌐 When manipulating known entities, it is possible to parametrize `Data` types for better type safety and code completion.

#### 已知文件 {#known-documents}

🌐 Known documents

```typescript
const ALL_CATEGORIES = ['food', 'tech', 'travel'];

function validateArticle(article: Data.ContentType<'api::article.article'>) {
  const { title, category } = article;
  //       ^?         ^?
  //       string     Data.ContentType<'api::category.category'>

  if (title.length < 5) {
    throw new Error('Title too short');
  }

  if (!ALL_CATEGORIES.includes(category.name)) {
    throw new Error(`Unknown category ${category.name}`);
  }
}
```

#### 已知组件 {#known-components}

🌐 Known components

```typescript
function processUsageMetrics(
  id: string,
  metrics: Data.Component<'app.metrics'>
) {
  telemetry.send(id, { clicks: metrics.clicks, views: metrics.views });
}
```

### 高级用例 {#advanced-use-cases}

🌐 Advanced use cases

<br/>

#### 实体子集 {#entities-subsets}

🌐 Entities subsets

使用类型的第二个参数（`TKeys`），可以获得实体的一个子集。

🌐 Using the types' second parameter (`TKeys`), it is possible to obtain a subset of an entity.

```typescript
type Credentials = Data.ContentType<'api::account.account', 'email' | 'password'>;
//   ^? { email: string; password: string }
```

```typescript
type UsageMetrics = Data.Component<'app.metrics', 'clicks' | 'views'>;
//   ^? { clicks: number; views: number }
```

#### 类型参数推断 {#type-argument-inference}

🌐 Type argument inference

可以根据其他函数参数绑定和限制实体类型。

🌐 It is possible to bind and restrict an entity type based on other function parameters.

在以下示例中，`uid` 类型在用作 `T` 时被推断，并作为 `document` 的类型参数使用。

🌐 In the following example, the `uid` type is inferred upon usage as `T` and used as a type parameter for the `document`.

```typescript

function display<T extends UID.ContentType>(
  uid: T,
  document: Data.ContentType<T>
) {
  switch (uid) {
    case 'api::article.article': {
      return document.title;
      //              ^? string
      //     ^? Data.ContentType<'api::article.article'>
    }
    case 'api::category.category': {
      return document.name;
      //              ^? string
      //     ^? Data.ContentType<'api::category.category'>
    }
    case 'api::account.account': {
      return document.email;
      //              ^? string
      //     ^? Data.ContentType<'api::account.account'>
    }
    default: {
      throw new Error(`unknown content-type uid: "${uid}"`);
    }
  }
}
```

在调用函数时，`document` 类型需要与给定的 `uid` 匹配。

🌐 When calling the function, the `document` type needs to match the given `uid`.

```typescript
declare const article: Data.Document<'api::article.article'>;
declare const category: Data.Document<'api::category.category'>;
declare const account: Data.Document<'api::account.account'>;

display('api::article.article', article);
display('api::category.category', category);
display('api::account.account', account);
// ^ ✅

display('api::article.article', category);
// ^ Error: "category" is not assignable to parameter of type ContentType<'api::article.article'>
```
