ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.71k stars 2.3k forks source link

[Bug]: Type error in VueNodeViewRenderer since version 2.7.0 #5657

Closed xiaweiss closed 2 months ago

xiaweiss commented 2 months ago

Affected Packages

core, vue

Version(s)

= 2.7.0

Bug Description

image image

Browser Used

Chrome

Code Example URL

No response

Expected Behavior

Type is correct

Additional Context (Optional)

No response

Dependency Updates

nperez0111 commented 2 months ago

Already reported https://github.com/ueberdosis/tiptap/issues/5632

nperez0111 commented 2 months ago

You do have to use new and your Component type is probably incorrect, but it is hard to see with just a screenshot

xiaweiss commented 2 months ago

Thank you, the code in screenshot works fine, it just has the typescript error, I can ignore it for now by adding // @ts-ignore

fallenfreq commented 1 month ago

This is still an issue for me with version 2.8.0 using examples from the website

This is the syntax-highlighting example from here: https://tiptap.dev/docs/examples/advanced/syntax-highlighting

and this component is simply being imported and passed to VueNodeViewRenderer and throwing the same type error as mentioned above. Using new should not be required and would only throw a different error.

<template>
  <NodeViewWrapper class="code-block">
    <select contenteditable="false" v-model="selectedLanguage">
      <option :value="null">auto</option>
      <option disabled>—</option>
      <option v-for="(language, index) in languages" :value="language" :key="index">
        {{ language }}
      </option>
    </select>
    <pre><code><NodeViewContent /></code></pre>
  </NodeViewWrapper>
</template>

<script>
import { NodeViewContent, nodeViewProps, NodeViewWrapper } from '@tiptap/vue-3'

export default {
  components: {
    NodeViewWrapper,
    NodeViewContent
  },

  props: nodeViewProps,

  data() {
    return {
      languages: this.extension.options.lowlight.listLanguages()
    }
  },

  computed: {
    selectedLanguage: {
      get() {
        return this.node.attrs.language
      },
      set(language) {
        this.updateAttributes({ language })
      }
    }
  }
}
</script>

Here is where the CodeBlockComponent is being passed to VueNodeViewRenderer which throws the type error. I am temporarily using "as" to cast the type to Component until this is fixed.

 CodeBlockLowlight
          .extend({
            addNodeView() {
              return VueNodeViewRenderer(CodeBlockComponent)
            },
          })
          .configure({ lowlight })
nperez0111 commented 1 month ago

You are right about not needing the new but it is not clear what the error is then. Can you share the full error?

I'd take a PR contribution here, ts types aren't very high priority

fallenfreq commented 1 month ago

Here is the full error, my current understanding and solution.

Argument of type:

DefineComponent<                                         
  {                                                      
    editor: { type: PropType<Editor>; required: true };  
    node: { type: PropType<Node>; required: true };      
    decorations: {                                       
      type: PropType<readonly DecorationWithType[]>;     
      required: true;                                    
    };                                                   
    /* 4 more */;                                        
    deleteNode: { ...; };                                
  },                                                     
  /* 11 more */,                                         
  {}                                                     
>                                                        

is not assignable to parameter of type 

 Component<NodeViewProps> 

Type:

DefineComponent<                                         
  {                                                      
    editor: { type: PropType<Editor>; required: true };  
    node: { type: PropType<Node>; required: true };      
    decorations: {                                       
      type: PropType<readonly DecorationWithType[]>;     
      required: true;                                    
    };                                                   
    /* 4 more */;                                        
    deleteNode: { ...; };                                
  },                                                     
  /* 11 more */,                                         
  {}                                                     
>                                                        

is not assignable to type:

ComponentPublicInstanceConstructor<  
  NodeViewProps,                     
  any,                               
  any,                               
  any,                               
  ComputedOptions,                   
  MethodOptions                      
>                                    

Type:

CreateComponentPublicInstance<                             
  Readonly<                                                
    ExtractPropTypes<{                                     
      editor: { type: PropType<Editor>; required: true };  
      node: { type: PropType<Node>; required: true };      
      decorations: {                                       
        type: PropType<readonly DecorationWithType[]>;     
        required: true;                                    
      };                                                   
      /* 4 more */;                                        
      deleteNode: { ...; };                                
    }>                                                     
  >,                                                       
  /* 18 more */,                                           
  {}                                                       
>                                                          

is missing the following properties from type

NodeViewProps 

• view • innerDecorations • HTMLAttributes

Solution

It could be that it's more of an issue with the documentation and that Typescript just struggles to infer the necessary types correctly but the Tiptap documents insinuate that passing nodeViewProps is enough to make it compatible with Typescript.

The issue is that nodeViewProps is not of the type NodeViewProps that is expected by VueNodeViewRenderer It is missing HTMLAttributes, view, and innerDecorations properties or at least Typescript is not able to infer them.

Using defineComponent with the options API is not enough to make the type inference work. However, using defineComponent with setup works as expected so that's what I am doing now.

Use this

return VueNodeViewRenderer(
  defineComponent({
    props: nodeViewProps,
    setup(props) {
      return () => h(TiptapCodeBlock, props)
    }
  })
)

instead of simply doing this

return VueNodeViewRenderer(TiptapCodeBlock)

There are a few cases where I use the component outside of the editor as well, in those cases, I wrap the component in NodeViewWrapper and use NodeViewContent using h directly within defineComponent.

component, contenteditable, contentAs are dynamic in my case below. I am just sharing this incase it helps anyone else out.

VueNodeViewRenderer(
  defineComponent({
    props: nodeViewProps,
    setup(props) {
      return () =>
        h(NodeViewWrapper, { contenteditable }, [
          h(component, props.node.attrs, {
            default: () => h(NodeViewContent, { ...(contentAs ? { as: contentAs } : {}) })
          })
        ])
    }
  })
)