Deno 風格指南
請注意,這是 Deno 執行階段和 Deno 標準函式庫中**內部執行階段程式碼**的風格指南。這並非 Deno 使用者的一般風格指南。
版權標頭 Jump to heading
儲存庫中的大多數模組都應具有以下版權標頭
// 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
設計函式介面時,請遵守以下規則。
-
作為公開 API 一部分的函式接受 0-2 個必要參數,加上(如有必要)一個選項物件(因此最多共 3 個)。
-
選用參數通常應放入選項物件中。
如果只有一個選用參數,並且似乎難以想像我們將在未來新增更多選用參數,則不在選項物件中的選用參數可能是可接受的。
-
「options」參數是唯一一個正規的「Object」。
其他參數可以是物件,但它們必須與「plain」Object 執行階段區分開來,方法是具有以下任一項
- 可區分的原型(例如
Array
、Map
、Date
、class 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.serve、Deno.test、Deno.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
沒有外部相依性,但我們仍然必須小心,保持內部相依性簡單且易於管理。特別是,請注意不要引入循環導入。
如果檔名以底線開頭:_foo.ts
,請勿連結到它 Jump to heading
在某些情況下,可能需要內部模組,但其 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.ts
。std
模組的測試應放入 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 引發的使用者導向錯誤訊息應清晰、簡潔且一致。錯誤訊息應使用句子大小寫,但不應以句點結尾。錯誤訊息應無文法錯誤和錯字,並以美式英語撰寫。
請注意,錯誤訊息風格指南仍在制定中,並非所有錯誤訊息都已更新以符合目前的風格。
應遵循的錯誤訊息風格
- 訊息應以大寫字母開頭
Bad: cannot parse input
Good: Cannot parse input
- 訊息不應以句點結尾
Bad: Cannot parse input.
Good: Cannot parse input
- 訊息應為字串值使用引號
Bad: Cannot parse input hello, world
Good: Cannot parse input "hello, world"
- 訊息應說明導致錯誤的動作
Bad: Invalid input x
Good: Cannot parse input x
- 應使用主動語態
Bad: Input x cannot be parsed
Good: Cannot parse input x
- 訊息不應使用縮寫
Bad: Can't parse input x
Good: Cannot parse input x
- 訊息在提供其他資訊時應使用冒號。永遠不應使用句點。其他標點符號可視需要使用
Bad: Cannot parse input x. value is empty
Good: Cannot parse input x: value is empty
- 其他資訊應描述目前狀態,如果可能,也應以肯定語氣描述期望狀態
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
。靜態頂層項目(例如 string
、number
、bigint
、boolean
、RegExp
、靜態項目陣列、靜態鍵值記錄等)使用 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]+$/;
當名稱使用 camelCase
或 PascalCase
時,即使它們的部分是首字母縮略字,也始終遵循它們的規則。
注意:Web API 使用大寫首字母縮略字 (JSON
、URL
、URL.createObjectURL()
等)。Deno 標準函式庫不遵循此慣例。
良好
class HttpObject {
}
不良
class HTTPObject {
}
良好
function convertUrl(url: URL) {
return url.href;
}
不良
function convertURL(url: URL) {
return url.href;
}