ban-type-assertions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBan Type Assertions
禁止类型断言
Enable with in a package and replace all casts with patterns the compiler can verify.
@typescript-eslint/consistent-type-assertionsassertionStyle: 'never'as X在包中启用并设置,将所有类型转换替换为编译器可验证的写法。
@typescript-eslint/consistent-type-assertionsassertionStyle: 'never'as XCore Philosophy
核心理念
Pick the strictly correct path, not the simpler one.
Every assertion is a spot where the developer told the compiler "trust me." The goal is to make the compiler verify instead. If you replace with a type guard that is equally unverified, you have not improved anything -- you have just moved the assertion.
asas Foo选择严格正确的方案,而非更简便的方案。
每个断言都是开发者告诉编译器“相信我”的地方。我们的目标是让编译器进行验证而非信任。如果你用同样未经验证的类型守卫替换,那并没有任何改进——只是把断言换了个位置而已。
asas FooQuick Reference
快速参考
- Rule:
@typescript-eslint/consistent-type-assertions - Config:
{ assertionStyle: 'never' } - Location:
packages/<name>/.eslintrc.js
- 规则:
@typescript-eslint/consistent-type-assertions - 配置:
{ assertionStyle: 'never' } - 位置:
packages/<name>/.eslintrc.js
Workflow
工作流程
1. Enable the Rule
1. 启用规则
Add to the package's :
.eslintrc.jsjs
rules: {
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
}在包的中添加:
.eslintrc.jsjs
rules: {
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
}2. Enumerate Violations
2. 枚举违规项
bash
cd packages/<name> && npm run lint 2>&1 | grep "consistent-type-assertions"Group violations by file and pattern before fixing.
bash
cd packages/<name> && npm run lint 2>&1 | grep "consistent-type-assertions"修复前按文件和模式对违规项进行分组。
3. Research Before Fixing
3. 修复前调研
Before writing any replacement code:
- Check for existing zod schemas -- grep for alongside the type name in
Schemaand across the repo.@factory/common - Check if schemas exist but aren't exported -- if so, export them rather than creating new ones.
- Check for duplicate types/interfaces across packages -- consolidate into if found.
@factory/common - Understand the data flow -- is this a parse boundary (external data), a narrowing site (union type), or a library type gap?
在编写任何替代代码前:
- 检查现有Zod模式——在及整个仓库中,结合类型名称搜索
@factory/common。Schema - 检查是否存在未导出的模式——如果存在,优先导出而非创建新的模式。
- 检查跨包的重复类型/接口——如果发现,合并到中。
@factory/common - 理解数据流——这是解析边界(外部数据)、收窄场景(联合类型)还是库类型缺失?
4. Fix Violations Using the Pattern Hierarchy
4. 按模式层级修复违规项
Tier 1: Zod Parsing (for external data boundaries)
层级1:Zod解析(用于外部数据边界)
Use for any data entering the system from JSON, disk, network, IPC, etc. This gives runtime validation, not just a type annotation.
typescript
// BAD
const data = JSON.parse(raw) as MyType;
// GOOD
const data = MySchema.parse(JSON.parse(raw));Use when you need to handle errors gracefully (e.g., returning an error response with context like a request id):
safeParsetypescript
// BAD: throws before you can extract the request id
const request = RequestSchema.parse(JSON.parse(raw));
// GOOD: safeParse lets you return a proper error
const parsed = RequestSchema.safeParse(JSON.parse(raw));
if (!parsed.success) {
return errorResponse(rawObj?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
const request = parsed.data;适用于从JSON、磁盘、网络、IPC等进入系统的任何数据。这提供运行时验证,而非仅类型注解。
typescript
// 不良写法
const data = JSON.parse(raw) as MyType;
// 良好写法
const data = MySchema.parse(JSON.parse(raw));当需要优雅处理错误时(例如返回包含请求ID等上下文的错误响应),使用:
safeParsetypescript
// 不良写法:在提取请求ID前就抛出错误
const request = RequestSchema.parse(JSON.parse(raw));
// 良好写法:safeParse允许返回合适的错误
const parsed = RequestSchema.safeParse(JSON.parse(raw));
if (!parsed.success) {
return errorResponse(rawObj?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
const request = parsed.data;Tier 2: Control Flow Narrowing (for union types)
层级2:控制流收窄(用于联合类型)
Use , , , or discriminated unions:
switchininstanceoftypescript
// BAD
(error as NodeJS.ErrnoException).code
// GOOD
if (error instanceof Error && 'code' in error) {
const code = error.code;
}typescript
// BAD
if (METHODS.has(method as Method)) { ... }
// GOOD: switch narrows exhaustively
switch (method) {
case 'foo':
case 'bar':
return handle(method); // narrowed
}使用、、或可辨识联合:
switchininstanceoftypescript
// 不良写法
(error as NodeJS.ErrnoException).code
// 良好写法
if (error instanceof Error && 'code' in error) {
const code = error.code;
}typescript
// 不良写法
if (METHODS.has(method as Method)) { ... }
// 良好写法:switch可穷尽收窄类型
switch (method) {
case 'foo':
case 'bar':
return handle(method); // 类型已收窄
}Tier 3: eslint-disable with Justification (last resort)
层级3:eslint-disable加说明(最后手段)
Only for genuinely unavoidable cases (library type gaps, generic parameters that can't be inferred). Always explain why:
typescript
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ws library types require generic parameter
ws.on('message', handler);仅适用于真正无法避免的场景(库类型缺失、无法推断的泛型参数)。务必说明原因:
typescript
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ws库类型需要泛型参数
ws.on('message', handler);Anti-Pattern: Type Guards That Are Disguised Assertions
反模式:伪装成类型守卫的断言
typescript
// NOT an improvement -- checks shape but not content
function isDaemonRequest(x: unknown): x is DaemonRequest {
return typeof x === 'object' && x !== null && 'method' in x;
}A zod schema validates values. A type guard like this is an unverified assertion with extra steps. Only use type guards when the narrowing logic is truly sufficient.
typescript
// 无改进——仅检查形状而非内容
function isDaemonRequest(x: unknown): x is DaemonRequest {
return typeof x === 'object' && x !== null && 'method' in x;
}Zod模式会验证值,而此类类型守卫只是多了步骤的未经验证断言。仅当收窄逻辑真正充分时才使用类型守卫。
5. Use Strict Schemas, Not Permissive Ones
5. 使用严格模式,而非宽松模式
When a schema exists (e.g., ), use it strictly rather than . This ensures forward compatibility -- if fields are removed in a migration, stale data gets cleaned on read.
SessionSettingsSchemaz.record(z.unknown())typescript
// BAD: accepts anything
const settings = z.record(z.unknown()).parse(raw);
// GOOD: validates against the real shape
const settings = SessionSettingsSchema.parse(raw);当模式已存在时(例如),严格使用该模式而非。这确保向前兼容性——如果迁移中移除了字段,陈旧数据会在读取时被清理。
SessionSettingsSchemaz.record(z.unknown())typescript
// 不良写法:接受任何内容
const settings = z.record(z.unknown()).parse(raw);
// 良好写法:根据真实结构验证
const settings = SessionSettingsSchema.parse(raw);6. Promote Shared Schemas to @factory/common
@factory/common6. 将共享模式推广到@factory/common
@factory/commonIf you find duplicate interfaces, types, or schemas across packages, consolidate them:
- Create the schema in
@factory/common/<domain>/<subdomain>/schema.ts - Put any enums in a sibling (required by
enums.ts)factory/enum-file-organization - Export via a subpath (e.g., ), not the barrel
@factory/common/session/summaryindex.ts - Delete all local duplicates
- Update all consumers to import from the common subpath
- Run at repo root to catch unused barrel re-exports
npm run knip
如果发现跨包的重复接口、类型或模式,将其合并:
- 在中创建模式
@factory/common/<domain>/<subdomain>/schema.ts - 将所有枚举放在同级的中(
enums.ts规则要求)factory/enum-file-organization - 通过子路径导出(例如),不要通过桶文件
@factory/common/session/summary导出index.ts - 删除所有本地重复项
- 更新所有消费者从公共子路径导入
- 在仓库根目录运行捕获未使用的桶文件重导出
npm run knip
7. Fix Test Mocks to Match Schemas
7. 修改测试模拟数据以匹配模式
Once you replace with , test mocks that relied on the assertion will fail validation. Fix the mocks -- do not disable the rule in tests.
as X.parse()Create helper functions to centralize valid test fixtures:
typescript
function mockSessionSummary(
overrides?: Partial<SessionSummaryEvent>,
): SessionSummaryEvent {
return {
type: 'session_start',
id: 'test-id',
title: 'Test Session',
owner: 'test-owner',
...overrides,
};
}一旦将替换为,依赖断言的测试模拟数据会验证失败。修改模拟数据——不要在测试中禁用规则。
as X.parse()创建辅助函数集中管理有效的测试 fixtures:
typescript
function mockSessionSummary(
overrides?: Partial<SessionSummaryEvent>,
): SessionSummaryEvent {
return {
type: 'session_start',
id: 'test-id',
title: 'Test Session',
owner: 'test-owner',
...overrides,
};
}8. Parse at the Boundary, Inside Error Handling
8. 在边界处解析,在错误处理内解析
Make sure parsing happens where failures produce proper error responses, not unhandled exceptions:
typescript
// BAD: parse outside try/catch -- if it throws, you lose context
const request = RequestSchema.parse(data);
try { handle(request); } catch { ... }
// GOOD: safeParse before try, handle error with context
const parsed = RequestSchema.safeParse(data);
if (!parsed.success) {
return errorResponse(rawData?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
try { handle(parsed.data); } catch { ... }确保解析发生在能生成合适错误响应的位置,而非未处理的异常:
typescript
// 不良写法:在try/catch外解析——如果抛出错误,会丢失上下文
const request = RequestSchema.parse(data);
try { handle(request); } catch { ... }
// 良好写法:先safeParse,再带上下文处理错误
const parsed = RequestSchema.safeParse(data);
if (!parsed.success) {
return errorResponse(rawData?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
try { handle(parsed.data); } catch { ... }Verification
验证
Run for all affected packages (a change in can break downstream lint):
@factory/commonbash
undefined针对所有受影响的包运行(中的变更可能破坏下游lint):
@factory/commonbash
undefinedLint (all affected packages)
检查代码规范(所有受影响包)
cd packages/<name> && npm run lint
cd packages/<name> && npm run lint
Typecheck
类型检查
npm run typecheck
npm run typecheck
Tests
测试
npm run test
npm run test
Unused exports (repo root)
未使用导出(仓库根目录)
npm run knip
undefinednpm run knip
undefinedReminders
注意事项
- requires TypeScript enums to live in files named
factory/enum-file-organizationenums.ts - prevents re-exporting types from barrel files -- consumers must import from the subpath directly
no-barrel-files - When promoting types to common, add a exports entry for the new subpath if one doesn't exist
package.json - Test overrides for the rule in may be needed if test files use assertion syntax in mock setup -- but prefer fixing mocks over disabling the rule
.eslintrc.js
- 规则要求TypeScript枚举必须放在名为
factory/enum-file-organization的文件中enums.ts - 规则禁止从桶文件重导出类型——消费者必须直接从子路径导入
no-barrel-files - 将类型推广到公共包时,如果新子路径对应的导出项不存在,需添加该条目
package.json - 如果测试文件在模拟数据设置中使用断言语法,可能需要在中覆盖规则——但优先修改模拟数据而非禁用规则
.eslintrc.js