Deno 风格指南
请注意,这是 Deno 运行时和 Deno 标准库中内部运行时代码的风格指南。 这并非旨在作为 Deno 用户的一般风格指南。
版权声明头 跳转到标题
存储库中的大多数模块都应具有以下版权声明头
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
如果代码源自其他地方,请确保文件具有正确的版权声明头。 我们只允许 MIT、BSD 和 Apache 许可的代码。
文件名中使用下划线,而不是破折号 跳转到标题
示例:使用 file_server.ts
而不是 file-server.ts
。
为新功能添加测试 跳转到标题
每个模块都应包含或附带其公共功能的测试。
TODO 注释 跳转到标题
TODO 注释通常应包含问题或作者的 github 用户名(在括号中)。 示例
// TODO(ry): Add tests.
// TODO(#123): Support Windows.
// FIXME(#349): Sometimes panics.
不鼓励元编程。包括使用 Proxy 跳转到标题
要明确,即使这意味着更多代码。
在某些情况下,使用此类技术可能是有意义的,但在绝大多数情况下并非如此。
包容性代码 跳转到标题
请遵循 https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/inclusive_code.md 中概述的包容性代码指南。
Rust 跳转到标题
遵循 Rust 约定并与现有代码保持一致。
TypeScript 跳转到标题
代码库的 TypeScript 部分是标准库 std
。
使用 TypeScript 而不是 JavaScript 跳转到标题
不要使用文件名 index.ts
/index.js
跳转到标题
Deno 不会以特殊方式处理 “index.js” 或 “index.ts”。 通过使用这些文件名,它表明可以从模块说明符中省略它们,但实际上不能。 这会造成混淆。
如果代码目录需要默认入口点,请使用文件名 mod.ts
。 文件名 mod.ts
遵循 Rust 的约定,比 index.ts
更短,并且没有任何关于它可能如何工作的先入为主的观念。
导出的函数:最多 2 个参数,其余的放入 options 对象 跳转到标题
在设计函数接口时,请遵守以下规则。
-
作为公共 API 一部分的函数最多接受 0-2 个必需参数,外加(如果需要)一个 options 对象(因此最多总共 3 个)。
-
可选参数通常应放入 options 对象中。
如果只有一个可选参数,并且我们认为将来不太可能添加更多可选参数,则不放在 options 对象中的可选参数可能是可以接受的。
-
“options” 参数是唯一一个常规的 “Object” 参数。
其他参数可以是对象,但它们必须与 “plain” Object 运行时区分开来,方法是具有以下任一特征:
- 可区分的原型(例如
Array
、Map
、Date
、class MyThing
)。 - 众所周知的符号属性(例如,带有
Symbol.iterator
的可迭代对象)。
即使 options 对象的位置发生变化,这也允许 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 等示例。 另请参阅 此帖子。
导出所有用作导出成员参数的接口 跳转到标题
每当您使用包含在导出成员的参数或返回类型中的接口时,都应导出所使用的接口。 这是一个示例
// 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";
最小化依赖关系;不要进行循环导入 跳转到标题
虽然 std
没有外部依赖项,但我们仍然必须小心保持内部依赖项的简单性和可管理性。 特别是,注意不要引入循环导入。
如果文件名以下划线开头:_foo.ts
,则不要链接到它 跳转到标题
在某些情况下,可能需要内部模块,但其 API 并非旨在稳定或链接到。 在这种情况下,请在其前面加上下划线。 按照约定,只有其自身目录中的文件才应导入它。
为导出的符号使用 JSDoc 跳转到标题
我们力求提供完整的文档。 理想情况下,每个导出的符号都应有一行文档。
如果可能,请为 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");
* ```
*/
代码示例不应包含额外的注释,并且不得缩进。 它已经在注释内。 如果需要进一步的注释,则它不是一个好的示例。
使用指令解决代码检查问题 跳转到标题
目前,构建过程使用 dlint
来验证代码中的代码检查问题。 如果任务需要的代码不符合代码检查器,请使用 deno-lint-ignore <code>
指令来禁止警告。
// deno-lint-ignore no-explicit-any
let x: any;
这确保了持续集成过程不会因代码检查问题而失败,但应谨慎使用。
每个模块都应该附带一个测试模块 跳转到标题
每个具有公共功能的模块 foo.ts
都应附带一个测试模块 foo_test.ts
。 std
模块的测试应放在 std/tests
中,因为它们的上下文不同; 否则,它应该只是被测模块的同级模块。
单元测试应该明确 跳转到标题
为了更好地理解测试,函数应该被正确命名,因为它会在整个测试命令中提示。 像这样
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" });
});
注意:有关更多信息,请参阅 跟踪问题。
顶层函数不应使用箭头语法 跳转到标题
顶层函数应使用 function
关键字。 箭头语法应仅限于闭包。
错误
export const foo = (): string => {
return "bar";
};
良好
export function foo(): string {
return "bar";
}
错误消息 跳转到标题
从 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 跳转到标题
不要依赖外部代码。 跳转到标题
https://jsr.deno.org.cn/@std
旨在成为所有 Deno 程序都可以依赖的基线功能。 我们希望向用户保证此代码不包含可能未经审查的第三方代码。
记录并维护浏览器兼容性。 跳转到标题
如果模块与浏览器兼容,请在模块顶部的 JSDoc 中包含以下内容
// This module is browser-compatible.
通过不使用全局 Deno
命名空间或进行功能测试来维护此类模块的浏览器兼容性。 确保任何新的依赖项也与浏览器兼容。
优先使用 # 而不是 private 关键字 跳转到标题
在标准模块代码库中,我们更喜欢私有字段 (#
) 语法而不是 TypeScript 的 private
关键字。 私有字段使属性和方法即使在运行时也是私有的。 另一方面,TypeScript 的 private
关键字仅在编译时保证其私有性,并且字段在运行时是公开可访问的。
良好
class MyClass {
#foo = 1;
#bar() {}
}
错误
class MyClass {
private foo = 1;
private bar() {}
}
命名约定 跳转到标题
对于函数、方法、字段和局部变量,请使用 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;
}