deno.com
本頁面內容

Deno 風格指南

注意

請注意,這是 Deno 執行階段和 Deno 標準函式庫中**內部執行階段程式碼**的風格指南。這並非 Deno 使用者的一般風格指南。

儲存庫中的大多數模組都應具有以下版權標頭

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

如果程式碼源自其他地方,請確保檔案具有正確的版權標頭。我們僅允許 MIT、BSD 和 Apache 授權的程式碼。

檔名使用底線,而非破折號 Jump to heading

範例:使用 file_server.ts 而非 file-server.ts

為新功能新增測試 Jump to heading

每個模組都應包含或附帶其公開功能的測試。

TODO 註解 Jump to heading

TODO 註解通常應包含問題單號或作者的 github 使用者名稱(在括號中)。範例

// TODO(ry): Add tests.
// TODO(#123): Support Windows.
// FIXME(#349): Sometimes panics.

不鼓勵使用元編程。包括使用 Proxy Jump to heading

請明確表示,即使這意味著更多程式碼。

在某些情況下,使用此類技術可能是有意義的,但在絕大多數情況下並非如此。

具包容性的程式碼 Jump to heading

請遵循 https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/inclusive_code.md 中概述的具包容性程式碼指南。

Rust Jump to heading

遵循 Rust 慣例並與現有程式碼保持一致。

TypeScript Jump to heading

程式碼庫的 TypeScript 部分是標準函式庫 std

使用 TypeScript 而非 JavaScript Jump to heading

請勿使用檔名 index.ts/index.js Jump to heading

Deno 不會以特殊方式處理 "index.js" 或 "index.ts"。使用這些檔名會暗示它們可以從模組指定符中省略,但事實並非如此。這會造成混淆。

如果程式碼目錄需要預設進入點,請使用檔名 mod.ts。檔名 mod.ts 遵循 Rust 的慣例,比 index.ts 更短,並且沒有任何關於它可能如何運作的先入為主的觀念。

導出的函式:最多 2 個參數,其餘放入選項物件 Jump to heading

設計函式介面時,請遵守以下規則。

  1. 作為公開 API 一部分的函式接受 0-2 個必要參數,加上(如有必要)一個選項物件(因此最多共 3 個)。

  2. 選用參數通常應放入選項物件中。

    如果只有一個選用參數,並且似乎難以想像我們將在未來新增更多選用參數,則不在選項物件中的選用參數可能是可接受的。

  3. 「options」參數是唯一一個正規的「Object」。

    其他參數可以是物件,但它們必須與「plain」Object 執行階段區分開來,方法是具有以下任一項

    • 可區分的原型(例如 ArrayMapDateclass MyThing)。
    • 眾所周知的符號屬性(例如,具有 Symbol.iterator 的可迭代物件)。

    這允許 API 以向後相容的方式演進,即使選項物件的位置發生變化。

// BAD: optional parameters not part of options object. (#2)
export function resolve(
  hostname: string,
  family?: "ipv4" | "ipv6",
  timeout?: number,
): IPAddress[] {}
// GOOD.
export interface ResolveOptions {
  family?: "ipv4" | "ipv6";
  timeout?: number;
}
export function resolve(
  hostname: string,
  options: ResolveOptions = {},
): IPAddress[] {}
export interface Environment {
  [key: string]: string;
}

// BAD: `env` could be a regular Object and is therefore indistinguishable
// from an options object. (#3)
export function runShellWithEnv(cmdline: string, env: Environment): string {}

// GOOD.
export interface RunShellOptions {
  env: Environment;
}
export function runShellWithEnv(
  cmdline: string,
  options: RunShellOptions,
): string {}
// BAD: more than 3 arguments (#1), multiple optional parameters (#2).
export function renameSync(
  oldname: string,
  newname: string,
  replaceExisting?: boolean,
  followLinks?: boolean,
) {}
// GOOD.
interface RenameOptions {
  replaceExisting?: boolean;
  followLinks?: boolean;
}
export function renameSync(
  oldname: string,
  newname: string,
  options: RenameOptions = {},
) {}
// BAD: too many arguments. (#1)
export function pwrite(
  fd: number,
  buffer: ArrayBuffer,
  offset: number,
  length: number,
  position: number,
) {}
// BETTER.
export interface PWrite {
  fd: number;
  buffer: ArrayBuffer;
  offset: number;
  length: number;
  position: number;
}
export function pwrite(options: PWrite) {}

注意:當其中一個參數是函式時,您可以彈性調整順序。請參閱 Deno.serveDeno.testDeno.addSignalListener 等範例。另請參閱 這篇文章

匯出所有用作導出成員參數的介面 Jump to heading

每當您使用包含在導出成員的參數或傳回類型中的介面時,都應匯出使用的介面。以下是一個範例

// my_file.ts
export interface Person {
  name: string;
  age: number;
}

export function createPerson(name: string, age: number): Person {
  return { name, age };
}

// mod.ts
export { createPerson } from "./my_file.ts";
export type { Person } from "./my_file.ts";

最小化相依性;不要建立循環導入 Jump to heading

儘管 std 沒有外部相依性,但我們仍然必須小心,保持內部相依性簡單且易於管理。特別是,請注意不要引入循環導入。

在某些情況下,可能需要內部模組,但其 API 並非旨在穩定或連結。在這種情況下,請以底線作為前綴。依照慣例,只有其自身目錄中的檔案應導入它。

為導出的符號使用 JSDoc Jump to heading

我們力求提供完整的文件。理想情況下,每個導出的符號都應有一行文件說明。

如果可以,請為 JSDoc 使用單行。範例

/** foo does bar. */
export function foo() {
  // ...
}

文件易於人類閱讀非常重要,但也需要提供額外的樣式資訊,以確保產生的文件具有更豐富的文字。因此,JSDoc 通常應遵循 markdown 標記來豐富文字。

雖然 markdown 支援 HTML 標籤,但在 JSDoc 區塊中是禁止使用的。

程式碼字串文字應使用反引號 (`) 而非引號括起來。例如

/** Import something from the `deno` module. */

除非函式引數的意圖不明顯(但如果意圖不明顯,則應考慮 API),否則請勿記錄函式引數。因此,通常不應使用 @param。如果使用 @param,則不應包含 type,因為 TypeScript 已經是強型別的。

/**
 * Function with non-obvious param.
 * @param foo Description of non-obvious parameter.
 */

應盡可能縮小垂直間距。因此,單行註解應寫成

/** This is a good single-line JSDoc. */

而非

/**
 * This is a bad single-line JSDoc.
 */

程式碼範例應使用 markdown 格式,如下所示

/** A straightforward comment and an example:
 * ```ts
 * import { foo } from "deno";
 * foo("bar");
 * ```
 */

程式碼範例不應包含額外註解,且不得縮排。它已經在註解內。如果需要更多註解,則這不是一個好的範例。

使用指示詞解決程式碼檢查問題 Jump to heading

目前,建置過程使用 dlint 來驗證程式碼中的程式碼檢查問題。如果任務需要不符合程式碼檢查器的程式碼,請使用 deno-lint-ignore <code> 指示詞來抑制警告。

// deno-lint-ignore no-explicit-any
let x: any;

這可確保持續整合過程不會因程式碼檢查問題而失敗,但應謹慎使用。

每個模組都應附帶一個測試模組 Jump to heading

每個具有公開功能的模組 foo.ts 都應附帶一個測試模組 foo_test.tsstd 模組的測試應放入 std/tests 中,因為它們的上下文不同;否則,它應該只是被測試模組的同級模組。

單元測試應明確 Jump to heading

為了更好地理解測試,函式名稱應正確命名,因為它會在整個測試命令中提示。例如

foo() returns bar object ... ok

測試範例

import { assertEquals } from "@std/assert";
import { foo } from "./mod.ts";

Deno.test("foo() returns bar object", function () {
  assertEquals(foo(), { bar: "bar" });
});

注意:有關更多資訊,請參閱 追蹤問題

頂層函式不應使用箭頭語法 Jump to heading

頂層函式應使用 function 關鍵字。箭頭語法應僅限於閉包。

不良

export const foo = (): string => {
  return "bar";
};

良好

export function foo(): string {
  return "bar";
}

錯誤訊息 Jump to heading

從 JavaScript/TypeScript 引發的使用者導向錯誤訊息應清晰、簡潔且一致。錯誤訊息應使用句子大小寫,但不應以句點結尾。錯誤訊息應無文法錯誤和錯字,並以美式英語撰寫。

注意

請注意,錯誤訊息風格指南仍在制定中,並非所有錯誤訊息都已更新以符合目前的風格。

應遵循的錯誤訊息風格

  1. 訊息應以大寫字母開頭
Bad: cannot parse input
Good: Cannot parse input
  1. 訊息不應以句點結尾
Bad: Cannot parse input.
Good: Cannot parse input
  1. 訊息應為字串值使用引號
Bad: Cannot parse input hello, world
Good: Cannot parse input "hello, world"
  1. 訊息應說明導致錯誤的動作
Bad: Invalid input x
Good: Cannot parse input x
  1. 應使用主動語態
Bad: Input x cannot be parsed
Good: Cannot parse input x
  1. 訊息不應使用縮寫
Bad: Can't parse input x
Good: Cannot parse input x
  1. 訊息在提供其他資訊時應使用冒號。永遠不應使用句點。其他標點符號可視需要使用
Bad: Cannot parse input x. value is empty
Good: Cannot parse input x: value is empty
  1. 其他資訊應描述目前狀態,如果可能,也應以肯定語氣描述期望狀態
Bad: Cannot compute the square root for x: value must not be negative
Good: Cannot compute the square root for x: current value is ${x}
Better: Cannot compute the square root for x as x must be >= 0: current value is ${x}

std Jump to heading

不要依賴外部程式碼。 Jump to heading

https://jsr.dev.org.tw/@std 旨在成為所有 Deno 程式都可以依賴的基準功能。我們希望向使用者保證,此程式碼不包含可能未經審查的第三方程式碼。

記錄並維護瀏覽器相容性。 Jump to heading

如果模組與瀏覽器相容,請在模組頂端的 JSDoc 中包含以下內容

// This module is browser-compatible.

透過不使用全域 Deno 命名空間或進行功能測試來維護此類模組的瀏覽器相容性。確保任何新的相依性也與瀏覽器相容。

偏好使用 # 而非 private 關鍵字 Jump to heading

在標準模組程式碼庫中,我們偏好使用私有欄位 (#) 語法,而不是 TypeScript 的 private 關鍵字。私有欄位使屬性和方法即使在執行階段也是私有的。另一方面,TypeScript 的 private 關鍵字僅在編譯時保證它是私有的,並且這些欄位在執行階段是可公開存取的。

良好

class MyClass {
  #foo = 1;
  #bar() {}
}

不良

class MyClass {
  private foo = 1;
  private bar() {}
}

命名慣例 Jump to heading

函式、方法、欄位和區域變數使用 camelCase。類別、類型、介面和列舉使用 PascalCase。靜態頂層項目(例如 stringnumberbigintbooleanRegExp、靜態項目陣列、靜態鍵值記錄等)使用 UPPER_SNAKE_CASE

良好

function generateKey() {}

let currentValue = 0;

class KeyObject {}

type SharedKey = {};

enum KeyType {
  PublicKey,
  PrivateKey,
}

const KEY_VERSION = "1.0.0";

const KEY_MAX_LENGTH = 4294967295;

const KEY_PATTERN = /^[0-9a-f]+$/;

不良

function generate_key() {}

let current_value = 0;

function GenerateKey() {}

class keyObject {}

type sharedKey = {};

enum keyType {
  publicKey,
  privateKey,
}

const key_version = "1.0.0";

const key_maxLength = 4294967295;

const KeyPattern = /^[0-9a-f]+$/;

當名稱使用 camelCasePascalCase 時,即使它們的部分是首字母縮略字,也始終遵循它們的規則。

注意:Web API 使用大寫首字母縮略字 (JSONURLURL.createObjectURL() 等)。Deno 標準函式庫不遵循此慣例。

良好

class HttpObject {
}

不良

class HTTPObject {
}

良好

function convertUrl(url: URL) {
  return url.href;
}

不良

function convertURL(url: URL) {
  return url.href;
}

您找到需要的資訊了嗎?

隱私權政策