@codeforbreakfast/eslint-effect

中文文档
0.8.5发布于 2025/12/11MITES Module零依赖构建来源已验证近 30 天下载 2662

执行功能编程最佳做法的效果项目ESLint规则和配置

中文使用文档
文档目录30 节
  1. 安装
  2. 特征
  3. 自定义规则
  4. no-unnecessary-pipe-wrapper
  5. no-eta-expansion
  6. no-unnecessary-function-alias
  7. prefer-match-tag
  8. prefer-match-over-conditionals
  9. prefer-effect-if-over-match-boolean
  10. prefer-match-over-ternary
  11. no-switch-statement
  12. no-if-statement
  13. prefer-schema-validation-over-assertions
  14. suggest-currying-opportunity
  15. no-intermediate-effect-variables
  16. 规则预设
  17. 建议的预设
  18. 选入配置
  19. 功能可变性
  20. 使用量
  21. 建议设置
  22. 添加意见规则
  23. 有功能可变性
  24. 禁用个别规则
  25. 可用出口
  26. 默认导出( 建议)
  27. 已命名的出口
  28. 范围导入
  29. 哲学
  30. 许可证

@ codeforbreakfast/ slint 无效

ESLint规则和配置用于执行功能编程最佳做法和平庸效应代码模式的Effect项目.

安装

npm install --save-dev @codeforbreakfast/eslint-effect
# or
bun add --dev @codeforbreakfast/eslint-effect

特征

本套件提供:

  • 自定义 ESLint 规则 特定效果模式
  • 语法限制 执行职能方案拟订原则
  • 预设规则集 不同背景
  • 功能不可变规则 适应效果类型

自定义规则

no-unnecessary-pipe-wrapper

在单管操作周围检测到不必要的功能包接.

恶:

import { pipe } from 'effect';

declare const transform: (x: unknown) => unknown;

const fn = (x: unknown) => pipe(x, transform);

不错:

declare const transform: (x: unknown) => unknown;

const fn = transform;

no-eta-expansion

检测到不必要的函数包(eta-expansion),它只将参数直接传递到另一个函数. 也被称为"更优先的无分地风格"

恶:

import { Console } from 'effect';

declare const doSomething: (x: number) => unknown;
declare const processData: (a: string, b: number) => unknown;

const logError = (msg: string) => Console.error(msg);
const transform = (x: number) => doSomething(x);
const handler = (a: string, b: number) => processData(a, b);

不错:

import { Console } from 'effect';

declare const doSomething: (x: number) => unknown;
declare const processData: (a: string, b: number) => unknown;

const logError = Console.error;
const transform = doSomething;
const handler = processData;

理由:在功能编程中,去除(Xx.f(x) f)消除了不必要的间接. 当一个函数仅直接传递其参数给另一个函数而没有任何变换时,包裹会不增加值并降低可读性.

no-unnecessary-function-alias

检测到不必要功能别名, 不提供语义值 。 当一个常数被直接指派到另一个函数而不增加清晰度或抽象度时,应当在调用站点被内置.

恶:

import { Array, Ref } from 'effect';

declare const ref: Ref.Ref<unknown>;
declare const arr: readonly unknown[];
declare function useOnce(val: unknown): void;
declare function useTwice(val: unknown): void;

const getState = Ref.get;
const transform = Array.map;

// Used only once or twice
useOnce(getState(ref));
useTwice(transform(arr, (x: unknown) => (x as number) * 2));

不错:

import { Array, Ref, Stream } from 'effect';

declare const ref: Ref.Ref<unknown>;
declare const arr: readonly unknown[];
declare function useOnce(val: unknown): void;
declare function useTwice(val: unknown): void;

// Inline when used infrequently
useOnce(Ref.get(ref));
useTwice(Array.map(arr, (x: unknown) => (x as number) * 2));

// OR keep the alias if it adds semantic value
// eslint-disable-next-line effect/no-unnecessary-function-alias -- Alias clarifies stream processing context
const collectStreamResults = Stream.runCollect;

配置 :

  • maxReferences (默认 : 2- 在别名被认为有正当理由之前最多参考次数
{
  rules: {
    'effect/no-unnecessary-function-alias': ['warn', {
      maxReferences: 3,  // Allow aliases used 3+ times
    }]
  }
}

理由: 仅使用一次或两次的函数别名会增加认知间接费用而无有意义的好处. 它们迫使读者从别名定义到使用网站之间跳出. 直接函数调用更直接. 然而,经常使用(3+次)或通过名称来增加语义清晰度的别名应当保留.

prefer-match-tag

强制执行 Match.tag() 结束 Match.when() 页:1 _tag 歧视者.

恶:

import { Match, pipe } from 'effect';

type Tagged = { _tag: 'Success' } | { _tag: 'Error' };
declare const value: Tagged;
declare const handler: (val: { _tag: 'Success' }) => unknown;

pipe(Match.value(value), Match.when({ _tag: 'Success' }, handler));

不错:

import { Match, pipe } from 'effect';

type Tagged = { _tag: 'Success' } | { _tag: 'Error' };
declare const value: Tagged;
declare const handler: (val: { _tag: 'Success' }) => unknown;

pipe(Match.value(value), Match.tag('Success', handler));

prefer-match-over-conditionals

鼓励声明 Match 模式相对于必要 if 效果回调中的语句.

恶:

import { Effect, Match, pipe } from 'effect';

type TaggedValue = { _tag: 'Success' } | { _tag: 'Error' };
declare const handleSuccess: (x: { _tag: 'Success' }) => Effect.Effect<unknown>;
declare const handleError: (x: TaggedValue) => Effect.Effect<unknown>;
declare const effectValue: Effect.Effect<TaggedValue>;

pipe(
  effectValue,
  Effect.flatMap((x: TaggedValue) => {
    if (x._tag === 'Success') return handleSuccess(x);
    return handleError(x);
  })
);

不错:

import { Effect, Match, pipe } from 'effect';

type TaggedValue = { _tag: 'Success' } | { _tag: 'Error' };
declare const handleSuccess: (x: { _tag: 'Success' }) => Effect.Effect<unknown>;
declare const handleError: (x: TaggedValue) => Effect.Effect<unknown>;
declare const effectValue: Effect.Effect<TaggedValue>;

pipe(
  effectValue,
  Effect.flatMap((x: TaggedValue) =>
    pipe(Match.value(x), Match.tag('Success', handleSuccess), Match.orElse(handleError))
  )
);

prefer-effect-if-over-match-boolean

强制执行 Effect.if 结束 Match.value 对于布尔条件. Match.value 应保留在多个值上进行模式匹配,而非简单的真/假分支.

恶:

import { Effect, Match, pipe } from 'effect';

declare const isValid: boolean;
declare const user: { isAdmin: boolean };
declare function grantAccess(): Effect.Effect<unknown>;
declare function denyAccess(): Effect.Effect<unknown>;

pipe(
  isValid,
  Match.value,
  Match.when(true, () => Effect.succeed('valid')),
  Match.when(false, () => Effect.fail('invalid')),
  Match.exhaustive
);

pipe(
  user.isAdmin,
  Match.value,
  Match.when(true, () => grantAccess()),
  Match.when(false, () => denyAccess()),
  Match.exhaustive
);

不错:

import { Effect } from 'effect';

declare const isValid: boolean;
declare const user: { isAdmin: boolean };
declare function grantAccess(): Effect.Effect<unknown>;
declare function denyAccess(): Effect.Effect<unknown>;

Effect.if(isValid, {
  onTrue: () => Effect.succeed('valid'),
  onFalse: () => Effect.fail('invalid'),
});

Effect.if(user.isAdmin, {
  onTrue: () => grantAccess(),
  onFalse: () => denyAccess(),
});

理由数字 : Match.value 旨在对受歧视的工会和多种价值观进行模式匹配。 用它来做简单的布尔条件 是动词和模糊的意图. Effect.if 明确、简洁和清晰地传达布尔分支逻辑.

prefer-match-over-ternary

鼓励声明 Match.value 当模式匹配非布尔值时,或者在函数调用之间选择时,模式会超越三位运算符.

说明: 对于布尔条件返回效果,使用 Effect.if 相反(见 prefer-effect-if-over-match-boolean (见上文规则).

恶:

import { Option, Stream } from 'effect';

type FilterFn = (v: unknown) => boolean;
declare const filter: FilterFn | undefined;
declare const stream: Stream.Stream<unknown>;
declare function processValue(val: number): unknown;
declare function getDefault(): unknown;
declare function finalizeTask(): unknown;
declare function continueTask(): unknown;

const result = filter !== undefined ? Stream.filter(stream, filter) : stream;

const handler = (option: Option.Option<number>) =>
  Option.isSome(option) ? processValue(option.value) : getDefault();

const processStatus = (status: 'pending' | 'active' | 'complete') =>
  status === 'complete' ? finalizeTask() : continueTask();

不错:

import { Match, Option, pipe, Stream } from 'effect';

type FilterFn = (v: unknown) => boolean;
declare const filter: FilterFn | undefined;
declare const stream: Stream.Stream<unknown>;
declare function processValue(val: number): unknown;
declare function getDefault(): unknown;
declare function finalizeTask(): unknown;
declare function continueTask(): unknown;

const result = pipe(
  filter,
  Match.value,
  Match.when(Match.undefined, () => stream),
  Match.orElse((f) => Stream.filter(stream, f))
);

const handler = (option: Option.Option<number>) =>
  pipe(
    option,
    Match.value,
    Match.when({ _tag: 'Some' }, (opt) => processValue(opt.value)),
    Match.orElse(getDefault)
  );

const processStatus = (status: 'pending' | 'active' | 'complete') =>
  pipe(status, Match.value, Match.when('complete', finalizeTask), Match.orElse(continueTask));

对于带效果的布尔条件,使用 Effect.if数字 :

import { Effect } from 'effect';

declare const condition: boolean;

// Boolean condition returning Effects - use Effect.if, NOT Match.value
const result = Effect.if(condition, {
  onTrue: () => Effect.succeed(42),
  onFalse: () => Effect.fail('error'),
});

理由: 三级操作员是必须的,而且比 Match 模式。 使用 Match.value 为可选类型、联盟类型或非布尔条件等值的图案匹配提供了更好的可比较性。 但是,对于简单的布尔条件 返回效果, Effect.if 因为它更清晰 更安全 纯值之三(不是函数调用)是允许的,因为它们适合简单的值选择.

no-switch-statement

功能效应代码中的禁用交换语句. 切换语句是必须的,不提供详尽无遗的检查. 使用 Match 用于类型安全、详尽无遗的模式匹配.

恶:

import { Match, pipe } from 'effect';

type Event = { type: 'Created' } | { type: 'Updated' } | { type: 'Other' };
declare const event: Event;
declare function handleCreated(e: Event): unknown;
declare function handleUpdated(e: Event): unknown;
declare function handleUnknown(e: Event): unknown;

function processEvent() {
  switch (event.type) {
    case 'Created':
      return handleCreated(event);
    case 'Updated':
      return handleUpdated(event);
    default:
      return handleUnknown(event);
  }
}

不错:

import { Match, pipe } from 'effect';

type Event = { type: 'Created' } | { type: 'Updated' } | { type: 'Other' };
declare const event: Event;
declare function handleCreated(e: Event): unknown;
declare function handleUpdated(e: Event): unknown;
declare function handleUnknown(e: Event): unknown;

pipe(
  Match.value(event),
  Match.when({ type: 'Created' }, handleCreated),
  Match.when({ type: 'Updated' }, handleUpdated),
  Match.orElse(handleUnknown)
);

受歧视的工会 _tag数字 :

import { Match, pipe } from 'effect';

type TaggedEither = { _tag: 'Right'; value: unknown } | { _tag: 'Left'; error: unknown };
declare const either: TaggedEither;
declare function handleRight(val: { _tag: 'Right'; value: unknown }): unknown;
declare function handleLeft(val: { _tag: 'Left'; error: unknown }): unknown;

pipe(
  Match.value(either),
  Match.tag('Right', handleRight),
  Match.tag('Left', handleLeft),
  Match.exhaustive
);

no-if-statement

如果功能效应代码中的语句, 则禁止此语句 。 如果声明是必须的 并且没有很好的构思。 使用 Effect.if 对有效果的布尔条件, Match.value 用于图案匹配,或用于声明控制流的特定类型匹配器.

恶:

import { Effect, Option, pipe } from 'effect';

declare const todoState: { deleted: boolean };
declare const event: { type: string; data: { title: string } };
declare const option: Option.Option<string>;
declare const state: Option.Option<{ deleted: boolean }>;
declare const defaultValue: string;

// Bad pattern 1
function check1() {
  if (todoState.deleted) {
    return Effect.fail(new Error('Cannot complete deleted TODO'));
  }
  return Effect.succeed(todoState);
}

// Bad pattern 2
function check2() {
  if (event.type === 'TodoCreated') {
    return Effect.succeed({ title: event.data.title, completed: false });
  }
  return pipe(state, Option.match({ onNone: () => defaultValue, onSome: (s) => s }));
}

// Bad pattern 3
function check3() {
  if (Option.isSome(option)) {
    return option.value;
  }
  return defaultValue;
}

不错:

import { Effect, Option, Match, pipe } from 'effect';

declare const todoState: { deleted: boolean };
declare const event: { type: string; data: { title: string } };
declare const option: Option.Option<string>;
declare const state: Option.Option<{ deleted: boolean }>;
declare const defaultValue: string;
declare const condition: boolean;

// Boolean conditionals with Effects
Effect.if(todoState.deleted, {
  onTrue: () => Effect.fail(new Error('Cannot complete deleted TODO')),
  onFalse: () => Effect.succeed(todoState),
});

// Pattern matching on discriminated unions
pipe(
  event,
  Match.value,
  Match.when({ type: 'TodoCreated' }, (e) =>
    Effect.succeed({ title: e.data.title, completed: false })
  ),
  Match.orElse((e) => pipe(state, Option.match({ onNone: () => defaultValue, onSome: (s) => s })))
);

// Option/Either with built-in matchers
pipe(
  option,
  Option.getOrElse(() => defaultValue)
);

// Simple value selection - use ternary operators
const x = condition ? 'yes' : 'no';

理由:如果语句打破了功能构成,使代码更难解释. 它们没有提供图案匹配的详尽无遗的检查,而且不能轻易地由管子组成. 使用 Effect.if, (中文(简体) ), Match.value,或者特定类型匹配者喜欢 Option.match 提供更好的类型安全、详尽无遗的检查和可容性。 初等运算符仍然被允许进行简单的值选择,因为它们适合纯值.

prefer-schema-validation-over-assertions

Effect recalls中的不鼓励类型断言有利于运行时间验证.

恶:

import { Effect } from 'effect';

type MyType = { value: string };
declare function handler(x: MyType): unknown;

Effect.map((x: unknown) => handler(x as MyType));

不错:

import { Effect, pipe, Schema } from 'effect';

type MyType = { value: string };
declare const MyTypeSchema: Schema.Schema<MyType>;
declare const input: Effect.Effect<unknown>;
declare function handler(x: MyType): unknown;

pipe(input, Effect.flatMap(Schema.decodeUnknown(MyTypeSchema)), Effect.map(handler));

suggest-currying-opportunity

建议 curry 用户定义的功能去除管链中的箭头功能包. 默认情况下,仅在参数已经处于正确顺序(不需要重新排序)时才会建议咖喱,并且限制单级咖喱的可读性.

QQ 将建议( 好模式 - 已到尾端的参数 ) :

import { Effect } from 'effect';

declare const myEffect: Effect.Effect<unknown>;
declare function logError(message: string, error: unknown): Effect.Effect<void>;

// Before
Effect.catchAll(myEffect, (error) => logError('Failed', error));

// After currying (params already in right order)
const logErrorCurried = (message: string) => (error: unknown) =>
  Effect.sync(() => console.error(message, error));

Effect.catchAll(myEffect, logErrorCurried('Failed'));

QQ 不通过默认建议( 需要参数重排) :

import { Effect } from 'effect';

declare const myEffect: Effect.Effect<unknown>;
declare function logError(error: unknown, message: string): Effect.Effect<void>;

// This would break semantic order - error should come before message
Effect.catchAll(myEffect, (error) => logError(error, 'Failed'));

// Would require changing to: logError = (message) => (error) => ...
// But semantically, error should come first in the return object

{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}不会以默认的方式提出 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}会创造出深层的咖喱声:

import { Effect } from 'effect';

declare const myEffect: Effect.Effect<unknown>;
declare function processData(prefix: string, suffix: string, data: unknown): unknown;

// Would create 2-level currying: (prefix) => (suffix) => (data) => ...
Effect.map(myEffect, (data) => processData('prefix', 'suffix', data));

// Too many parentheses at call site: processData('prefix')('suffix')

配置选项 :

  • allowReordering (默认 : false- 即使参数需要重新排序,也允许提出建议
  • maxCurriedParams (默认 : 1,最大值 : 3) - 可建议的最大曲尾参数数
{
  rules: {
    'effect/suggest-currying-opportunity': ['warn', {
      allowReordering: true,    // Allow parameter reordering suggestions
      maxCurriedParams: 2,      // Allow up to 2-level currying
    }]
  }
}

说明: 此规则只触发用户定义的函数,而不是效果库函数.

no-intermediate-effect-variables

被禁者存储 Effect/Stream/pipe,当它们被内接入管链时会产生中间变量. 允许配置数据,如调度常数、重试政策和作为参数通过的效果值.

坏( 应当内含) :

import { Effect, pipe, Schema } from 'effect';

type User = { id: string; name: string };
declare const userId: string;
declare const UserSchema: Schema.Schema<User>;
declare const data: unknown;
declare function fetchUser(id: string): Effect.Effect<User>;
declare function validateUser(u: User): Effect.Effect<User>;
declare function formatUserData(u: User): unknown;
declare function processUser(u: User): User;
declare function saveToDatabase(u: User): Effect.Effect<void>;

// Breaking up the pipe chain for a single use
const userEffect = fetchUser(userId);
const result = pipe(userEffect, Effect.flatMap(validateUser), Effect.map(formatUserData));

// Storing intermediate pipe results used once
function processData() {
  const validated = pipe(data, Schema.decodeUnknown(UserSchema));
  const processed = Effect.map(validated, processUser);
  return Effect.flatMap(processed, saveToDatabase);
}

// Schema defined but only used once
const UserIdSchema = pipe(Schema.String, Schema.nonEmptyString());
export const decodeUserId = Schema.decode(UserIdSchema); // Only usage

好( 内置管道值) :

import { Effect, pipe, Schema } from 'effect';

type User = { id: string; name: string };
declare const userId: string;
declare const UserSchema: Schema.Schema<User>;
declare const data: unknown;
declare function fetchUser(id: string): Effect.Effect<User>;
declare function validateUser(u: User): Effect.Effect<User>;
declare function formatUserData(u: User): unknown;
declare function processUser(u: User): User;
declare function saveToDatabase(u: User): Effect.Effect<void>;

// Single cohesive pipe chain
const result = pipe(fetchUser(userId), Effect.flatMap(validateUser), Effect.map(formatUserData));

// All transformations in one composition
function processData() {
  return pipe(
    data,
    Schema.decodeUnknown(UserSchema),
    Effect.map(processUser),
    Effect.flatMap(saveToDatabase)
  );
}

// Schema inlined directly
export const decodeUserId = Schema.decode(pipe(Schema.String, Schema.nonEmptyString()));

好(配置数据):

import { Effect, pipe, Schedule } from 'effect';

declare const operation: Effect.Effect<unknown>;

// Schedule constants used as configuration
const retrySchedule = pipe(
  Schedule.exponential('100 millis'),
  Schedule.union(Schedule.spaced('1 second'))
);
const result = pipe(operation, Effect.retry(retrySchedule));

// Effect values used as parameters
const defaultValue = Effect.succeed(42);
const combined = pipe(
  Effect.succeed(1),
  Effect.zipWith(defaultValue, (a, b) => a + b)
);

好(多种用途 -- -- 合法再利用):

import { Context, Effect, Layer, pipe } from 'effect';

declare class PgClientService extends Context.Tag('PgClient')<PgClientService, unknown>() {}
declare const createPgClient: Effect.Effect<unknown>;
declare const Migration: { layer: Layer.Layer<unknown> };
declare const EventStore: { layer: Layer.Layer<unknown> };
declare function it(name: string, fn: () => unknown): void;

// Layer reused in multiple compositions
const PgLive = Layer.effect(PgClientService, createPgClient);
const Layer1 = pipe(Migration.layer, Layer.provide(PgLive)); // Use 1
const Layer2 = pipe(EventStore.layer, Layer.provide(PgLive)); // Use 2

// Effect reused across test cases
const setupTest = Effect.gen(function* () {
  yield* Effect.void;
});
it('test 1', () =>
  pipe(
    setupTest,
    Effect.flatMap(() => Effect.void)
  )); // Use 1
it('test 2', () =>
  pipe(
    setupTest,
    Effect.flatMap(() => Effect.void)
  )); // Use 2

好( 执行结果为纯值 ) :

import { Effect, Either, pipe } from 'effect';

type Request = { token: string };
declare const req: Request;
declare function authenticate(r: Request): Effect.Effect<string, Error>;

// runSync returns a plain value, not an Effect
function handleAuth() {
  const authResult = Effect.runSync(pipe(authenticate(req), Effect.either));
  if (Either.isLeft(authResult)) {
    return { json: () => ({ error: authResult.left }) };
  }
  return { json: () => ({ token: authResult.right }) };
}

理由: 中间管线变量会断流管组成. 诸如作为Effect 操作参数的调度常数和效应值等配置数据是合法的组成部分,应当提取. 多种用途也证明可变提取是合理的.

说明: 这是一条侵略性规则,包含在 pipeStrict 相会.

规则预设

建议用于大多数项目 - 核心效果最佳做法,没有有争议的规则:

  • 所有效果插件规则
  • 禁止类(效果标记/错误除外)
  • 无效 Effect.runSync编号 :runPromise 生产
  • 强制执行 Effect.andThen() 结束 Effect.flatMap(() => )
  • 强制执行 Effect.as() 结束 Effect.map(() => value)
  • 基于禁药方法 .pipe() (单独使用) pipe()(中文(简体) )
  • 禁止 curried 函数调用( 使用) pipe 改为)
  • 在转换中禁止身份功能
  • 执行适当的管道参数命令

不包括:

  • Effect.gen 禁用( 选择使用 noGen(中文(简体) )
  • _tag 访问禁入( 选择 ) preferMatch(中文(简体) )
  • 严格的管线规则(可选入) pipeStrict(中文(简体) )

strict

对于狂热者 - 所有东西 recommended 加上所有有意见的规则:

  • 全体 recommended 规则
  • 无效 Effect.gen (使用时 pipe 改为)
  • 直接禁止 _tag 访问(使用类型守护或 Match(中文(简体) )
  • 全部禁止 switch 语句( 使用 Match.value 用于图案匹配)
  • 全部禁止 if 语句( 使用 Effect.if, (中文(简体) ), Match.value,或类型特定匹配者)
  • 禁忌筑巢 pipe() 电话
  • 禁止多个 pipe() 在一个函数中呼叫

选入配置

把这些和 recommended 要定制您的规则 :

noGen

无效 Effect.gen 赞成: pipe 组成. 有争议 - 有些球队更喜欢金!

preferMatch

直接禁止 _tag 访问, 全部 switch 发言,以及所有发言 if 发言。 鼓励声明 Match 模式, Effect.if 用于布尔条件和特定类型匹配者.

pipeStrict

执行严格的管材构成规则:

  • 禁忌筑巢 pipe() 电话
  • 禁止多个 pipe() 每个函数的呼叫
  • 禁止将管道/效果储存在单一用途中间变量中

plugin

插件规则, 没有语法限制 .

功能可变性

functionalImmutabilityRules

积分 eslint-plugin-functional 带有效果感知配置 :

  • 执行只读类型,但效果类型除外
  • 无效 let 约束
  • 执行不可改变的数据模式
  • 禁用循环( 使用) Effect.forEach, (中文(简体) ), Array.map页:1

使用量

开始 recommended 对于合理的默认 :

import effectPlugin from '@codeforbreakfast/eslint-effect';

export default [
  {
    ...effectPlugin.configs.recommended,
    files: ['**/*.ts', '**/*.tsx'],
    plugins: {
      effect: effectPlugin,
    },
  },
];

添加意见规则

必要时选择采用更严格的规则:

import effectPlugin from '@codeforbreakfast/eslint-effect';

export default [
  {
    ...effectPlugin.configs.recommended,
    files: ['**/*.ts', '**/*.tsx'],
    plugins: {
      effect: effectPlugin,
    },
  },
  // Forbid Effect.gen
  {
    ...effectPlugin.configs.noGen,
    files: ['src/**/*.ts'],
  },
  // Enforce strict pipe rules
  {
    ...effectPlugin.configs.pipeStrict,
    files: ['src/**/*.ts'],
  },
  // Or just use strict for everything
  {
    ...effectPlugin.configs.strict,
    files: ['src/**/*.ts'],
    plugins: {
      effect: effectPlugin,
    },
  },
];

有功能可变性

要求数 eslint-plugin-functional数字 :

import functionalPlugin from 'eslint-plugin-functional';
import effectPlugin from '@codeforbreakfast/eslint-effect';

export default [
  {
    files: ['**/*.ts'],
    plugins: {
      functional: functionalPlugin,
    },
    rules: effectPlugin.configs.functionalImmutabilityRules,
  },
  {
    ...effectPlugin.configs.recommended,
    files: ['**/*.ts'],
    plugins: {
      effect: effectPlugin,
    },
  },
];

禁用个别规则

所有语法限制规则都作为命名为ESLint的规则被曝光,从而容易禁用特定规则:

import effectPlugin from '@codeforbreakfast/eslint-effect';

export default [
  {
    ...effectPlugin.configs.recommended,
    files: ['scripts/**/*.ts'],
    plugins: {
      effect: effectPlugin,
    },
    rules: {
      // Allow runPromise/runSync in scripts (application entry points)
      'effect/no-runPromise': 'off',
      'effect/no-runSync': 'off',
    },
  },
];

可用的命名规则 :

  • effect/no-classes - 无效类别,但效果标记/错误除外
  • effect/no-runSync - 无效效应. runSync
  • effect/no-runPromise - 无效效果. 运行保证
  • effect/prefer-andThen - 使用然后在平面图上使用(( ())...)
  • effect/prefer-as - 使用超度地图( () QQ值)
  • effect/no-gen - 强制效应.gen(通过 noGen 配置( c)
  • effect/no-unnecessary-pipe-wrapper - 检测不必要的管子包装器
  • effect/no-eta-expansion - 检测不必要的函数包装( 偏好无点样式)
  • effect/no-unnecessary-function-alias - 检测不必要的函数别名
  • effect/prefer-match-tag - 使用 Match.tag 代替 Match. when for tag
  • effect/prefer-match-over-conditionals - 如果声明使用匹配
  • effect/prefer-effect-if-over-match-boolean - 对布尔条件使用Effect.如果超过Match.值
  • effect/prefer-match-over-ternary - 使用 Match. value 超过三元运算符进行非布尔模式匹配
  • effect/no-switch-statement - 禁止所有开关语句(使用Match.value代替)
  • effect/no-if-statement - 如果语句(使用Effect.if、Match.value或类型特定匹配者代替),则全部禁止
  • effect/prefer-schema-validation-over-assertions - 使用Schema来代替类型断言
  • effect/suggest-currying-opportunity - 建议咖喱去除箭头功能包
  • effect/no-intermediate-effect-variables - 禁止储存管/影响结果,产生单用中间变量(通过 pipeStrict 配置( c)

可用出口

// @ts-nocheck - Package types available at runtime
import effectPlugin from '@codeforbreakfast/eslint-effect';

effectPlugin.rules; // All custom rules
effectPlugin.meta; // Plugin metadata

// Configs
effectPlugin.configs.recommended; // ⭐ Recommended for most projects
effectPlugin.configs.strict; // All rules including opinionated ones
effectPlugin.configs.noGen; // Opt-in: forbid Effect.gen
effectPlugin.configs.preferMatch; // Opt-in: forbid _tag access
effectPlugin.configs.pipeStrict; // Opt-in: strict pipe rules
effectPlugin.configs.plugin; // Plugin rules only
effectPlugin.configs.functionalImmutabilityRules; // Functional immutability rules object

每个配置( 除 functionalImmutabilityRules)是一个完整的ESLint平面配置对象,有:

  • name: 配置标识符
  • rules: 规则配置
  • plugins: 需要的插件(如适用)

已命名的出口

// @ts-nocheck - Package types available at runtime
import {
  rules, // All custom rules (for plugin registration)
  functionalImmutabilityRules, // Functional immutability rules object
} from '@codeforbreakfast/eslint-effect';

范围导入

import rules from '@codeforbreakfast/eslint-effect/rules';

哲学

这个软件包执行严格的功能编程风格,并带有效果:

  1. 代际组成页:1 pipe 组成情况 Effect.gen
  2. 类型安全: 使用运行时间验证(Schema)而不是类型断言
  3. 声明模式: 使用 Match 超过必要条件
  4. 可变性: 执行只读类型和永久数据模式
  5. 边界执行: 只在应用程序边界运行效果

许可证

麻省理工学院

版本与发布记录

@codeforbreakfast/eslint-effect 共发布 12 个版本,首次发布于 2025/10/06;过去 12 个月发布了 2 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.8.5latest2025/12/1110 个月前
0.8.42025/11/1511 个月前
0.8.32025/10/261 年前
0.8.22025/10/141.1 年前
0.8.12025/10/121.1 年前
0.8.02025/10/121.1 年前

关于 @codeforbreakfast/eslint-effect 的常见问题

@codeforbreakfast/eslint-effect 是什么?

执行功能编程最佳做法的效果项目ESLint规则和配置

如何安装 @codeforbreakfast/eslint-effect?

在项目目录运行 npm install @codeforbreakfast/eslint-effect,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:eslint(>=8.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@codeforbreakfast/eslint-effect 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/codeforbreakfast__eslint-effect 是否存在。

@codeforbreakfast/eslint-effect 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@codeforbreakfast/eslint-effect 可以商用吗?

@codeforbreakfast/eslint-effect 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@codeforbreakfast/eslint-effect 的下载量有多少?

根据 npm 官方下载统计,@codeforbreakfast/eslint-effect 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,662 次,日均约 89 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@codeforbreakfast/eslint-effect 还在维护吗?

最新版本 0.8.5 发布于 2025/12/11(10 个月前);至今共发布 12 个版本,其中过去 12 个月发布了 2 个;首次发布于 2025/10/06。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。