microsoft / vscode-prompt-tsx

MIT License
34 stars 1 forks source link

Build errors when another package in my monorepo uses react #74

Closed davidni closed 2 months ago

davidni commented 2 months ago

NOTE: Submitting this issue to help others who may run into this issue. I found a workaround which I am posting as part of this issue.

Issue description

When using @vscode/prompt-tsx in a monorepo, if another package in the monorepo uses react, the package that tries to use @vscode/prompt-tsx fails to build whenever it tries to use the facilities from this library.

The errors are nasty and not super helpful, such as:

src/chatParticipant/TeledrillChatParticipant.ts:48:9 - error TS2769: No overload matches this call.
  Overload 1 of 2, '(ctor: PromptElementCtor<PromptElementProps<Props>, any>, props: PromptElementProps<Props>, endpoint: IChatEndpointInfo, tokenizerMetadata: LanguageModelChat | ITokenizer, progress?: Progress<...> | undefined, token?: CancellationToken | undefined, mode?: "vscode" | undefined): Promise<...>', gave the following error.
    Argument of type 'typeof TeledrillPrompt' is not assignable to parameter of type 'PromptElementCtor<PromptElementProps<Props>, any>'.
      Construct signature return types 'TeledrillPrompt' and 'PromptElement<PromptElementProps<Props>, any>' are incompatible.
        The types returned by 'render(...)' are incompatible between these types.
          Type 'Element' is not assignable to type 'PromptPiece<any, any> | Promise<PromptPiece<any, any> | undefined> | undefined'.
  Overload 2 of 2, '(ctor: PromptElementCtor<PromptElementProps<Props>, any>, props: PromptElementProps<Props>, endpoint: IChatEndpointInfo, tokenizerMetadata: ITokenizer, progress?: Progress<...> | undefined, token?: CancellationToken | undefined, mode?: "none" | undefined): Promise<...>', gave the following error.
    Argument of type 'typeof TeledrillPrompt' is not assignable to parameter of type 'PromptElementCtor<PromptElementProps<Props>, any>'.

48         TeledrillPrompt,
           ~~~~~~~~~~~~~~~

src/chatParticipant/TeledrillPrompt.tsx:17:5 - error TS2416: Property 'render' in type 'TeledrillPrompt' is not assignable to the same property in base type 'PromptElement<Props, PromptState>'.
  Type '(state: PromptState, sizing: PromptSizing) => Element' is not assignable to type '(state: PromptState, sizing: PromptSizing) => PromptPiece<any, any> | Promise<PromptPiece<any, any> | undefined> | undefined'.
    Type 'Element' is not assignable to type 'PromptPiece<any, any> | Promise<PromptPiece<any, any> | undefined> | undefined'.
      Type 'ReactElement<any, any>' is missing the following properties from type 'PromptPiece<any, any>': ctor, children

17     render(state: PromptState, sizing: PromptSizing) {
       ~~~~~~

src/chatParticipant/TeledrillPrompt.tsx:20:18 - error TS2786: 'AssistantMessage' cannot be used as a JSX component.
  Its type 'typeof AssistantMessage' is not a valid JSX element type.
    Types of construct signatures are incompatible.
      Type 'new (props: ChatMessageProps) => AssistantMessage' is not assignable to type 'new (props: any, deprecatedLegacyContext?: any) => Component<any, any, any>'.
        Type 'AssistantMessage' is missing the following properties from type 'Component<any, any, any>': context, setState, forceUpdate, state, refs

20                 <AssistantMessage>
                    ~~~~~~~~~~~~~~~~

This is what the monorepo looks like (illustrative example). Notice that package @types/react was restored at the root level of the monorepo. This causes Typescript to fail typechecks when using @vscode/prompt-tsx. As a result, I am unable to use this library in a monorepo that also references react.

monorepo/
|-- node_modules/
|   \-- @types/
|       \-- react/
|           \-- ...
|-- package.json
\-- packages/
    |-- package1-uses-@vscode-prompt-tsx/
    |   |-- package.json
    |   \-- ..
    \-- package2-uses-react/
        |-- package.json
        \-- ..

Investigation and Workaround

The issue was due to Typescript’s default behavior to include all @types packages in the compilation. Since I was using @vscode/prompt-tsx in a repo where another package uses react, this was causing a parent folder’s node_modules to contain an @types/react folder, which Typescript would include (by design) and would conflict with @vscode/prompt-tsx.

Fixing turned out to be trivial. All I had to do was add this to my tsconfig:

{
  // ...
  "compilerOptions": {
    // ...
    "types": [] // <-- this was the fix!
  }
}

Explanation: See TypeScript: TSConfig Reference - Docs on every TSConfig option (typescriptlang.org):

By default all visible ”@types” packages are included in your compilation. _Packages in node_modules/@types of any enclosing folder are considered visible._ _For example, that means packages within ./node_modules/@types/, ../node_modules/@types/, ../../node_modules/@types/, and so on._

If types is specified, only packages listed will be included in the global scope. For instance:

  {
    "compilerOptions": {
      "types": ["node", "jest", "express"]
    }
  }

Expectations from this issue

It would be good to document this in the README so that adopters won't have to deal with this like I did. It may also be helpful to explore how to allow react and this library to coexist in case there is ever a scenario where both are needed in the same project.

alexdima commented 2 months ago

:+1: This also matches the change I had to do to our project:

Image