diff --git a/.changeset/unsafe-effect-type-assertion.md b/.changeset/unsafe-effect-type-assertion.md
new file mode 100644
index 00000000..364fe8c4
--- /dev/null
+++ b/.changeset/unsafe-effect-type-assertion.md
@@ -0,0 +1,7 @@
+---
+"@effect/tsgo": minor
+---
+
+Add the `unsafeEffectTypeAssertion` diagnostic and quick-fix to detect assertions that unsafely narrow Effect error or requirements channels.
+
+This also ports the matching v3/v4 examples, preview coverage, and baselines for the new rule.
diff --git a/README.md b/README.md
index 310b9460..1c17fd7c 100644
--- a/README.md
+++ b/README.md
@@ -89,6 +89,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu
preferSchemaOverJson | 💡 | | Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify | ✓ | ✓ |
processEnv | ➖ | | Warns when reading process.env outside Effect generators instead of using Effect Config | ✓ | ✓ |
processEnvInEffect | ➖ | | Warns when reading process.env inside Effect generators instead of using Effect Config | ✓ | ✓ |
+ unsafeEffectTypeAssertion | ➖ | 🔧 | Detects unsafe type assertions that narrow Effect error or requirements channels | ✓ | ✓ |
| Style Cleanup, consistency, and idiomatic Effect code. |
catchAllToMapError | 💡 | 🔧 | Suggests using Effect.mapError instead of Effect.catch + Effect.fail | ✓ | ✓ |
deterministicKeys | ➖ | 🔧 | Enforces deterministic naming for service/tag/error identifiers based on class names | ✓ | ✓ |
diff --git a/_packages/tsgo/src/metadata.json b/_packages/tsgo/src/metadata.json
index 604f2f3c..239327bc 100644
--- a/_packages/tsgo/src/metadata.json
+++ b/_packages/tsgo/src/metadata.json
@@ -45,7 +45,8 @@
"nodeBuiltinImport": "warning",
"preferSchemaOverJson": "warning",
"processEnv": "warning",
- "processEnvInEffect": "warning"
+ "processEnvInEffect": "warning",
+ "unsafeEffectTypeAssertion": "warning"
}
}
],
@@ -1313,6 +1314,30 @@
]
}
},
+ {
+ "name": "unsafeEffectTypeAssertion",
+ "group": "effectNative",
+ "description": "Detects unsafe type assertions that narrow Effect error or requirements channels",
+ "defaultSeverity": "off",
+ "fixable": true,
+ "supportedEffect": [
+ "v3",
+ "v4"
+ ],
+ "codes": [
+ 377075
+ ],
+ "preview": {
+ "sourceText": "import { Effect } from \"effect\"\n\ndeclare const program: Effect.Effect\u003cstring, \"boom\", \"service\"\u003e\n\nexport const preview = program as Effect.Effect\u003cstring, never, never\u003e\n",
+ "diagnostics": [
+ {
+ "start": 121,
+ "end": 167,
+ "text": "This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)"
+ }
+ ]
+ }
+ },
{
"name": "catchAllToMapError",
"group": "style",
diff --git a/internal/diagnostics/effectDiagnosticMessages.json b/internal/diagnostics/effectDiagnosticMessages.json
index 5debdeb4..2838cf19 100644
--- a/internal/diagnostics/effectDiagnosticMessages.json
+++ b/internal/diagnostics/effectDiagnosticMessages.json
@@ -99,6 +99,14 @@
"category": "Suggestion",
"code": 377074
},
+ "This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)": {
+ "category": "Warning",
+ "code": 377075
+ },
+ "The {0} channel is narrowed from `{1}` to `{2}`. effect(unsafeEffectTypeAssertion)": {
+ "category": "Warning",
+ "code": 377087
+ },
"This code uses `JSON.parse` or `JSON.stringify`. Effect Schema provides Effect-aware APIs for JSON parsing and stringifying. effect(preferSchemaOverJson)": {
"category": "Suggestion",
"code": 377026
diff --git a/internal/fixables/fixables.go b/internal/fixables/fixables.go
index f8f5c8ac..574c1d6b 100644
--- a/internal/fixables/fixables.go
+++ b/internal/fixables/fixables.go
@@ -31,6 +31,7 @@ var All = []fixable.Fixable{
OverriddenSchemaConstructorFix,
InstanceOfSchemaFix,
LayerMergeAllWithDependenciesFix,
+ UnsafeEffectTypeAssertionFix,
MissingEffectErrorCatchFix,
MultipleEffectProvideFix,
SchemaStructWithTagFix,
diff --git a/internal/fixables/unsafe_effect_type_assertion.go b/internal/fixables/unsafe_effect_type_assertion.go
new file mode 100644
index 00000000..cca817c7
--- /dev/null
+++ b/internal/fixables/unsafe_effect_type_assertion.go
@@ -0,0 +1,39 @@
+package fixables
+
+import (
+ "github.com/effect-ts/tsgo/internal/fixable"
+ "github.com/effect-ts/tsgo/internal/rewriter"
+ "github.com/effect-ts/tsgo/internal/rules"
+ tsdiag "github.com/microsoft/typescript-go/shim/diagnostics"
+ "github.com/microsoft/typescript-go/shim/ls"
+)
+
+var UnsafeEffectTypeAssertionFix = fixable.Fixable{
+ Name: "unsafeEffectTypeAssertion",
+ Description: "Remove the unsafe Effect assertion",
+ ErrorCodes: []int32{tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion.Code()},
+ FixIDs: []string{"unsafeEffectTypeAssertion_fix"},
+ Run: runUnsafeEffectTypeAssertionFix,
+}
+
+func runUnsafeEffectTypeAssertionFix(ctx *fixable.Context) []ls.CodeAction {
+ matches := rules.AnalyzeUnsafeEffectTypeAssertion(ctx.TypeParser, ctx.Checker, ctx.SourceFile)
+ for _, match := range matches {
+ diagRange := match.Location
+ if !diagRange.Intersects(ctx.Span) && !ctx.Span.ContainedBy(diagRange) {
+ continue
+ }
+
+ if action := ctx.NewFixAction(fixable.FixAction{
+ Description: "Remove the unsafe Effect assertion",
+ Run: func(tracker *rewriter.Tracker) {
+ tracker.ReplaceNode(ctx.SourceFile, match.AssertionNode, match.ExpressionNode, nil)
+ },
+ }); action != nil {
+ return []ls.CodeAction{*action}
+ }
+ return nil
+ }
+
+ return nil
+}
diff --git a/internal/rules/rules.go b/internal/rules/rules.go
index 73edd330..0dffd5e5 100644
--- a/internal/rules/rules.go
+++ b/internal/rules/rules.go
@@ -52,6 +52,7 @@ var All = []rule.Rule{
AsyncFunction,
ScopeInLayerEffect,
StrictEffectProvide,
+ UnsafeEffectTypeAssertion,
MultipleEffectProvide,
MissingLayerContext,
LayerMergeAllWithDependencies,
diff --git a/internal/rules/unsafe_effect_type_assertion.go b/internal/rules/unsafe_effect_type_assertion.go
new file mode 100644
index 00000000..f257b0ce
--- /dev/null
+++ b/internal/rules/unsafe_effect_type_assertion.go
@@ -0,0 +1,158 @@
+package rules
+
+import (
+ "github.com/effect-ts/tsgo/etscore"
+ "github.com/effect-ts/tsgo/internal/rule"
+ "github.com/effect-ts/tsgo/internal/typeparser"
+ "github.com/microsoft/typescript-go/shim/ast"
+ "github.com/microsoft/typescript-go/shim/checker"
+ "github.com/microsoft/typescript-go/shim/core"
+ tsdiag "github.com/microsoft/typescript-go/shim/diagnostics"
+ "github.com/microsoft/typescript-go/shim/scanner"
+)
+
+var UnsafeEffectTypeAssertion = rule.Rule{
+ Name: "unsafeEffectTypeAssertion",
+ Group: "effectNative",
+ Description: "Detects unsafe type assertions that narrow Effect error or requirements channels",
+ DefaultSeverity: etscore.SeverityOff,
+ SupportedEffect: []string{"v3", "v4"},
+ Codes: []int32{tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion.Code()},
+ Run: func(ctx *rule.Context) []*ast.Diagnostic {
+ matches := AnalyzeUnsafeEffectTypeAssertion(ctx.TypeParser, ctx.Checker, ctx.SourceFile)
+ diags := make([]*ast.Diagnostic, len(matches))
+ for i, match := range matches {
+ diags[i] = ctx.NewDiagnostic(
+ match.SourceFile,
+ match.Location,
+ tsdiag.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion,
+ unsafeEffectTypeAssertionRelatedInformation(ctx, match),
+ )
+ }
+ return diags
+ },
+}
+
+type UnsafeEffectTypeAssertionChannel struct {
+ Name string
+ Original string
+ Asserted string
+}
+
+type UnsafeEffectTypeAssertionMatch struct {
+ SourceFile *ast.SourceFile
+ Location core.TextRange
+ AssertionNode *ast.Node
+ ExpressionNode *ast.Node
+ LocationNode *ast.Node
+ Channels []UnsafeEffectTypeAssertionChannel
+}
+
+func AnalyzeUnsafeEffectTypeAssertion(tp *typeparser.TypeParser, c *checker.Checker, sf *ast.SourceFile) []UnsafeEffectTypeAssertionMatch {
+ if tp == nil || c == nil || sf == nil {
+ return nil
+ }
+
+ var matches []UnsafeEffectTypeAssertionMatch
+ nodesToVisit := make([]*ast.Node, 0)
+ pushChild := func(child *ast.Node) bool {
+ nodesToVisit = append(nodesToVisit, child)
+ return false
+ }
+ sf.AsNode().ForEachChild(pushChild)
+
+ for len(nodesToVisit) > 0 {
+ node := nodesToVisit[len(nodesToVisit)-1]
+ nodesToVisit = nodesToVisit[:len(nodesToVisit)-1]
+ node.ForEachChild(pushChild)
+
+ if node.Kind != ast.KindAsExpression && node.Kind != ast.KindTypeAssertionExpression {
+ continue
+ }
+
+ expr := node.Expression()
+ if expr == nil {
+ continue
+ }
+
+ originalType := tp.GetTypeAtLocation(expr)
+ assertedType := tp.GetTypeAtLocation(node)
+ if originalType == nil || assertedType == nil {
+ continue
+ }
+
+ originalEffect := tp.EffectType(originalType, expr)
+ if originalEffect == nil {
+ continue
+ }
+
+ assertedEffect := tp.EffectType(assertedType, node)
+ if assertedEffect == nil {
+ continue
+ }
+
+ channels := make([]UnsafeEffectTypeAssertionChannel, 0, 2)
+ if originalEffect.E != nil && assertedEffect.E != nil && !isAnyType(originalEffect.E) && !checker.Checker_isTypeAssignableTo(c, originalEffect.E, assertedEffect.E) {
+ channels = append(channels, UnsafeEffectTypeAssertionChannel{
+ Name: "error",
+ Original: c.TypeToString(originalEffect.E),
+ Asserted: c.TypeToString(assertedEffect.E),
+ })
+ }
+ if originalEffect.R != nil && assertedEffect.R != nil && !isAnyType(originalEffect.R) && !checker.Checker_isTypeAssignableTo(c, originalEffect.R, assertedEffect.R) {
+ channels = append(channels, UnsafeEffectTypeAssertionChannel{
+ Name: "requirements",
+ Original: c.TypeToString(originalEffect.R),
+ Asserted: c.TypeToString(assertedEffect.R),
+ })
+ }
+ if len(channels) == 0 {
+ continue
+ }
+
+ locationNode := node
+ if typeNode := node.Type(); typeNode != nil {
+ locationNode = typeNode
+ }
+
+ matches = append(matches, UnsafeEffectTypeAssertionMatch{
+ SourceFile: sf,
+ Location: scanner.GetErrorRangeForNode(sf, node),
+ AssertionNode: node,
+ ExpressionNode: expr,
+ LocationNode: locationNode,
+ Channels: channels,
+ })
+ }
+
+ return matches
+}
+
+func isAnyType(t *checker.Type) bool {
+ return t != nil && t.Flags()&checker.TypeFlagsAny != 0
+}
+
+func unsafeEffectTypeAssertionRelatedInformation(ctx *rule.Context, match UnsafeEffectTypeAssertionMatch) []*ast.Diagnostic {
+ if ctx == nil || len(match.Channels) == 0 {
+ return nil
+ }
+
+ locationNode := match.LocationNode
+ if locationNode == nil {
+ locationNode = match.AssertionNode
+ }
+
+ related := make([]*ast.Diagnostic, 0, len(match.Channels))
+ for _, channel := range match.Channels {
+ related = append(related, ctx.NewDiagnostic(
+ match.SourceFile,
+ scanner.GetErrorRangeForNode(match.SourceFile, locationNode),
+ tsdiag.The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion,
+ nil,
+ channel.Name,
+ channel.Original,
+ channel.Asserted,
+ ))
+ }
+ return related
+}
diff --git a/shim/diagnostics/shim.go b/shim/diagnostics/shim.go
index 29b64a68..c81db504 100644
--- a/shim/diagnostics/shim.go
+++ b/shim/diagnostics/shim.go
@@ -1607,6 +1607,7 @@ var Tagged_template_expressions_are_not_permitted_in_an_optional_chain = diagnos
var Target_allows_only_0_element_s_but_source_may_have_more = diagnostics.Target_allows_only_0_element_s_but_source_may_have_more
var Target_requires_0_element_s_but_source_may_have_fewer = diagnostics.Target_requires_0_element_s_but_source_may_have_fewer
var Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1 = diagnostics.Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1
+var The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion = diagnostics.The_0_channel_is_narrowed_from_1_to_2_effect_unsafeEffectTypeAssertion
var The_0_modifier_can_only_be_used_in_TypeScript_files = diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files
var The_0_operator_cannot_be_applied_to_type_symbol = diagnostics.The_0_operator_cannot_be_applied_to_type_symbol
var The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead = diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead
@@ -1824,6 +1825,7 @@ var This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_exp
var This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found = diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found
var This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0 = diagnostics.This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0
var This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0 = diagnostics.This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0
+var This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion = diagnostics.This_type_assertion_unsafely_narrows_the_Effect_error_or_requirements_channels_effect_unsafeEffectTypeAssertion
var This_type_parameter_might_need_an_extends_0_constraint = diagnostics.This_type_parameter_might_need_an_extends_0_constraint
var This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments = diagnostics.This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments
var This_uses_the_Effect_do_emulation_Effect_gen_or_Effect_fn_achieve_the_same_result_with_native_JS_scopes_effect_effectDoNotation = diagnostics.This_uses_the_Effect_do_emulation_Effect_gen_or_Effect_fn_achieve_the_same_result_with_native_JS_scopes_effect_effectDoNotation
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt
new file mode 100644
index 00000000..59e98a23
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.errors.txt
@@ -0,0 +1,36 @@
+=== Metadata ===
+Effect version: 3.19.19
+
+/.src/unsafeEffectTypeAssertion.ts(10,28): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+/.src/unsafeEffectTypeAssertion.ts(14,36): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+/.src/unsafeEffectTypeAssertion.ts(15,29): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+
+
+==== /.src/unsafeEffectTypeAssertion.ts (3 errors) ====
+ // @effect-diagnostics unsafeEffectTypeAssertion:warning
+ import { Effect } from "effect"
+
+ declare const program: Effect.Effect
+ declare const anyError: Effect.Effect
+ declare const anyRequirements: Effect.Effect
+ declare const noRequirements: Effect.Effect
+ declare const noError: Effect.Effect
+
+ export const narrowsBoth = program as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const skipsAnyError = anyError as Effect.Effect
+ export const skipsAnyRequirements = anyRequirements as Effect.Effect
+ export const safeWiden = program as Effect.Effect
+ export const narrowsRequirements = noError as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(14,47): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const narrowsError = noRequirements as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(15,47): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const notEffect = 1 as number
+
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt
new file mode 100644
index 00000000..10c94b4e
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.txt
@@ -0,0 +1 @@
+/.src/unsafeEffectTypeAssertion.ts -> unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
new file mode 100644
index 00000000..a3f2505c
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
@@ -0,0 +1,8 @@
+flowchart TB
+ 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/]
+ 1[/"type: Effect#lt;string, never, #quot;service#quot;#gt;
node: anyError as Effect.Effect#lt;string, never, #quot;service#quot;#gt;"/]
+ 2[/"type: Effect#lt;string, #quot;boom#quot;, never#gt;
node: anyRequirements as Effect.Effect#lt;string, #quot;boom#quot;, never#gt;"/]
+ 3[/"type: Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;
node: program as Effect.Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;"/]
+ 4[/"type: Effect#lt;string, never, never#gt;
node: noError as Effect.Effect#lt;string, never, never#gt;"/]
+ 5[/"type: Effect#lt;string, never, never#gt;
node: noRequirements as Effect.Effect#lt;string, never, never#gt;"/]
+ 6[/"type: number
node: 1 as number"/]
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt
new file mode 100644
index 00000000..e4b2c247
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.layers.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt
new file mode 100644
index 00000000..4f383896
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.pipings.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion.ts (0 flows) ====
diff --git a/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt
new file mode 100644
index 00000000..a3628506
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/unsafeEffectTypeAssertion.quickfixes.txt
@@ -0,0 +1,99 @@
+=== Quick Fix Inventory ===
+
+[D1] (10:28-10:74) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+[D2] (14:36-14:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+[D3] (15:29-15:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+=== Quick Fix Application Results ===
+
+=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D1] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
+
+
+=== [D2] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D2] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D2] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
+
+
+=== [D3] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D3] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D3] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements
+export const notEffect = 1 as number
+
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt
new file mode 100644
index 00000000..f7d62150
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.errors.txt
@@ -0,0 +1,36 @@
+=== Metadata ===
+Effect version: 4.0.0
+
+/.src/unsafeEffectTypeAssertion.ts(10,28): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+/.src/unsafeEffectTypeAssertion.ts(14,36): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+/.src/unsafeEffectTypeAssertion.ts(15,29): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+
+
+==== /.src/unsafeEffectTypeAssertion.ts (3 errors) ====
+ // @effect-diagnostics unsafeEffectTypeAssertion:warning
+ import { Effect } from "effect"
+
+ declare const program: Effect.Effect
+ declare const anyError: Effect.Effect
+ declare const anyRequirements: Effect.Effect
+ declare const noRequirements: Effect.Effect
+ declare const noError: Effect.Effect
+
+ export const narrowsBoth = program as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(10,39): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const skipsAnyError = anyError as Effect.Effect
+ export const skipsAnyRequirements = anyRequirements as Effect.Effect
+ export const safeWiden = program as Effect.Effect
+ export const narrowsRequirements = noError as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(14,47): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const narrowsError = noRequirements as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion.ts(15,47): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion)
+ export const notEffect = 1 as number
+
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt
new file mode 100644
index 00000000..10c94b4e
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.txt
@@ -0,0 +1 @@
+/.src/unsafeEffectTypeAssertion.ts -> unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
new file mode 100644
index 00000000..a3f2505c
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.flows.unsafeEffectTypeAssertion.mermaid
@@ -0,0 +1,8 @@
+flowchart TB
+ 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/]
+ 1[/"type: Effect#lt;string, never, #quot;service#quot;#gt;
node: anyError as Effect.Effect#lt;string, never, #quot;service#quot;#gt;"/]
+ 2[/"type: Effect#lt;string, #quot;boom#quot;, never#gt;
node: anyRequirements as Effect.Effect#lt;string, #quot;boom#quot;, never#gt;"/]
+ 3[/"type: Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;
node: program as Effect.Effect#lt;string, #quot;boom#quot; #124; #quot;other#quot;, #quot;service#quot;#gt;"/]
+ 4[/"type: Effect#lt;string, never, never#gt;
node: noError as Effect.Effect#lt;string, never, never#gt;"/]
+ 5[/"type: Effect#lt;string, never, never#gt;
node: noRequirements as Effect.Effect#lt;string, never, never#gt;"/]
+ 6[/"type: number
node: 1 as number"/]
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt
new file mode 100644
index 00000000..e4b2c247
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.layers.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt
new file mode 100644
index 00000000..4f383896
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.pipings.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion.ts (0 flows) ====
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt
new file mode 100644
index 00000000..a3628506
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion.quickfixes.txt
@@ -0,0 +1,99 @@
+=== Quick Fix Inventory ===
+
+[D1] (10:28-10:74) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+[D2] (14:36-14:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+[D3] (15:29-15:82) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+=== Quick Fix Application Results ===
+
+=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D1] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
+
+
+=== [D2] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D2] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D2] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
+
+
+=== [D3] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D3] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D3] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion.ts ---
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements
+export const notEffect = 1 as number
+
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt
new file mode 100644
index 00000000..ee9be326
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.errors.txt
@@ -0,0 +1,19 @@
+=== Metadata ===
+Effect version: 4.0.0
+
+/.src/unsafeEffectTypeAssertion_preview.ts(7,24): warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+
+
+==== /.src/unsafeEffectTypeAssertion_preview.ts (1 errors) ====
+ // @effect-diagnostics *:off
+ // @effect-diagnostics unsafeEffectTypeAssertion:warning
+ import { Effect } from "effect"
+
+ declare const program: Effect.Effect
+
+ export const preview = program as Effect.Effect
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! warning TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion_preview.ts(7,35): The error channel is narrowed from `"boom"` to `never`. effect(unsafeEffectTypeAssertion)
+!!! related TS377087 /.src/unsafeEffectTypeAssertion_preview.ts(7,35): The requirements channel is narrowed from `"service"` to `never`. effect(unsafeEffectTypeAssertion)
+
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt
new file mode 100644
index 00000000..701be638
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.txt
@@ -0,0 +1 @@
+/.src/unsafeEffectTypeAssertion_preview.ts -> unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid
new file mode 100644
index 00000000..563348eb
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.flows.unsafeEffectTypeAssertion_preview.mermaid
@@ -0,0 +1,2 @@
+flowchart TB
+ 0[/"type: Effect#lt;string, never, never#gt;
node: program as Effect.Effect#lt;string, never, never#gt;"/]
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt
new file mode 100644
index 00000000..6cf91914
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.layers.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion_preview.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt
new file mode 100644
index 00000000..51cc63d1
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.pipings.txt
@@ -0,0 +1 @@
+==== /.src/unsafeEffectTypeAssertion_preview.ts (0 flows) ====
diff --git a/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt
new file mode 100644
index 00000000..0066698b
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/unsafeEffectTypeAssertion_preview.quickfixes.txt
@@ -0,0 +1,26 @@
+=== Quick Fix Inventory ===
+
+[D1] (7:24-7:70) TS377075: This type assertion unsafely narrows the Effect error or requirements channels. effect(unsafeEffectTypeAssertion)
+ Fix 0: "Disable unsafeEffectTypeAssertion for this line"
+ Fix 1: "Disable unsafeEffectTypeAssertion for entire file"
+ Fix 2: "Remove the unsafe Effect assertion"
+
+=== Quick Fix Application Results ===
+
+=== [D1] Fix 0: "Disable unsafeEffectTypeAssertion for this line" ===
+skipped by default
+
+=== [D1] Fix 1: "Disable unsafeEffectTypeAssertion for entire file" ===
+skipped by default
+
+=== [D1] Fix 2: "Remove the unsafe Effect assertion" ===
+
+--- file:///.src/unsafeEffectTypeAssertion_preview.ts ---
+// @effect-diagnostics *:off
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+
+export const preview = program
+
diff --git a/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts b/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts
new file mode 100644
index 00000000..a2b6b1c5
--- /dev/null
+++ b/testdata/tests/effect-v3/unsafeEffectTypeAssertion.ts
@@ -0,0 +1,16 @@
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
diff --git a/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts b/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts
new file mode 100644
index 00000000..a2b6b1c5
--- /dev/null
+++ b/testdata/tests/effect-v4/unsafeEffectTypeAssertion.ts
@@ -0,0 +1,16 @@
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+declare const anyError: Effect.Effect
+declare const anyRequirements: Effect.Effect
+declare const noRequirements: Effect.Effect
+declare const noError: Effect.Effect
+
+export const narrowsBoth = program as Effect.Effect
+export const skipsAnyError = anyError as Effect.Effect
+export const skipsAnyRequirements = anyRequirements as Effect.Effect
+export const safeWiden = program as Effect.Effect
+export const narrowsRequirements = noError as Effect.Effect
+export const narrowsError = noRequirements as Effect.Effect
+export const notEffect = 1 as number
diff --git a/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts b/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts
new file mode 100644
index 00000000..7068fd1f
--- /dev/null
+++ b/testdata/tests/effect-v4/unsafeEffectTypeAssertion_preview.ts
@@ -0,0 +1,7 @@
+// @effect-diagnostics *:off
+// @effect-diagnostics unsafeEffectTypeAssertion:warning
+import { Effect } from "effect"
+
+declare const program: Effect.Effect
+
+export const preview = program as Effect.Effect