Effect-TS / effect

An ecosystem of tools for building production-grade applications in TypeScript.
https://effect.website
MIT License
7.05k stars 222 forks source link

From Discord: Troubleshooting Self-Referencing Annotations in Schema Classes #2592

Open effect-bot opened 4 months ago

effect-bot commented 4 months ago

Summary

The discussion revolves around a technical challenge encountered when using self-referencing annotations in schema classes within TypeScript, specifically in the context of the Effect-TS ecosystem. The participants are trying to troubleshoot issues related to self-referencing annotations, such as the pretty annotation in a JsonRpcError class, which seems to be problematic due to TypeScript's handling of type references.

Key points from the discussion include:

  1. Initial Problem: The issue arises when attempting to use self-referencing annotations in schema classes, which seems to break due to TypeScript's handling of self-references.

  2. Workaround Suggestion: A workaround involving the use of self: any is mentioned, though it's acknowledged as not ideal. Another workaround involves annotating inside a static override of the get ast() method, which allows for the use of self-referencing types in a more controlled manner.

  3. Diagnosis: The problem is speculated to be a TypeScript bug related to how class type self-references are handled, particularly in the context of function class constructors. The TypeScript error suggests an incorrect self-reference, even though the self-reference is not directly used to define the type.

  4. Technical Exploration: Various code snippets are shared to illustrate the issue and explore how TypeScript behaves with different approaches to defining and extending classes with self-referencing types. These examples highlight the limitations and behaviors of TypeScript's type system in this context.

  5. Potential TypeScript Bug: The consensus among the participants is that this behavior likely represents a bug in TypeScript, given that the self-referencing type is only used as a parameter in a function and does not directly influence the class's type definition. There's a suggestion to conduct further research and possibly report this issue to the TypeScript team for clarification or resolution.

  6. Humor and References: The conversation includes humorous references and light-hearted comments, indicating a friendly and collaborative effort to troubleshoot the technical issue.

Key Takeaways:

Discord thread

https://discord.com/channels/795981131316985866/1223260500843040873

Repro

import { Schema } from "@effect/schema"

// ts error: Type 'Foo' recursively references itself as a base type.ts(2310)
class Foo extends Schema.Class<Foo>("Foo")({
  value: Schema.Number
}, {
  pretty: () => (a) => `Foo(${a.value})`
}) {}

Workaround 1 (any annotation)

import { Pretty, Schema } from "@effect/schema"

class Foo extends Schema.Class<Foo>("Foo")({
  value: Schema.Number
}, {
  pretty: () => (a: any) => `Foo(${a.value})`
}) {}

console.log(Pretty.make(Foo)(new Foo({ value: 1 }))) // "Foo(1)"

Workaround 2 (ast override)

import { Pretty, Schema } from "@effect/schema"

class Foo extends Schema.Class<Foo>("Foo")({
  value: Schema.Number
}) {
  static override get ast() {
    return Schema.make<Foo>(super.ast).annotations({
      pretty: () => (_: Foo) => `Foo(${_.value})`
    }).ast
  }
}

console.log(Pretty.make(Foo)(new Foo({ value: 1 }))) // "Foo(1)"
gcanti commented 4 months ago

Solved upstream (ts 5.5.) https://github.com/microsoft/TypeScript/pull/58392