withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
46.13k stars 2.44k forks source link

TS warning for attributes dynamic HTML tags #6205

Closed EduardDopler closed 1 year ago

EduardDopler commented 1 year ago

What version of astro are you using?

2.0.10

Are you using an SSR adapter? If so, which one?

doesn't matter

What package manager are you using?

npm

What operating system are you using?

Mac

Describe the Bug

If you add the class attribute to a Dynamic HTML tag, you get a TypeScript error:

Type '{ children: string; class: string; }' is not assignable to type 'IntrinsicAttributes'. Property 'class' does not exist on type 'IntrinsicAttributes'.ts(2322)

ts-dynamic-tag-attribute

The code still works as expected (the class gets added to the element).

This error does not appear on Stackblitz but if you download and open in VS code, it becomes directly visible. In TS base and strict mode.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-vee7yu?file=src/pages/index.astro

Participation

mfrachet commented 1 year ago

Hello 👋🏻 . I might be wrong, but here are my assumptions:

since heading is declared and could be dynamic, I'm not sure TypeScript is able to do its magic by itself. I would personally force it to be the type I would like it to be.

It's verbose, but it passes TS checking:

type Heading = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";

const headingLevel = 2;
const TitleElement = (headingLevel > 0 ? "h" + headingLevel : "div") as Heading;

Maybe other folks with more experience in typescript have another solution though

Princesseuh commented 1 year ago

Our language tooling is currently a bit clunky regarding completions on those elements, but the type checking behaviour is accurate. As @mfrachet comment points out, TypeScript needs to be able to tell which tag you're using precisely.

EduardDopler commented 1 year ago

Makes sense, thank you for your help!

ggondim commented 1 year ago

@Princesseuh @EduardDopler I don't think it makes any sense using libraries which their components' tags are predictable.

I'm using PrimeVue and Vue.js with Astro and the linting reports PrimeVue's components as IntrinsicAttributes.

image

This is also reported by other users using SvelteKit: https://github.com/withastro/language-tools/issues/425#issuecomment-1542328124.

The Astro tooling should infer the tag type if it is imported, otherwise it should let other extensions to infer (like Volar/Vetur) when client:only is set to the specific language server.

Princesseuh commented 1 year ago

That's unfortunately not how language servers work. We can't defer things to anyone, we need to handle stuff ourselves (how I wish we didn't have to support Vue and Svelte ourselves, ha!). Even if we could defer, it wouldn't fix the issue as the types would still be incompatible in the greater scheme of things.

In this case, my best guess without investigating too much is the issue is that our tooling does not support getting props types from Vue components that are using the Options API. Supporting Vue's thousand ways to define props is complicated, we're still working on it.

The Svelte issue you linked is unrelated and specific to Svelte.

ggondim commented 1 year ago

@Princesseuh Ok, got it. It's sad how difficult it should be to support Vue Options API.

TYSM for your kind and sincere reply

4rc0s commented 10 months ago

Is this probably the reason for TS complaining when using Image's src property to display an image from a content collection?

types.d.ts: The expected type comes from property 'src' which is declared here on type 'IntrinsicAttributes & Props'

I tried forcing to as string but then TS complains:

Type '{ src: string; class: string; alt: string; }' is not assignable to type 'IntrinsicAttributes & Props'.
  Type '{ src: string; class: string; alt: string; }' is not assignable to type 'IntrinsicAttributes & { title?: string; 'class:list'?: string | Record<string, boolean> | Record<any, any> | Iterable<string> | Iterable<...>; ... 199 more ...; src: string; }'.ts(2322)
Hanawa02 commented 1 month ago

I'm having the same issue now with Astro 4.15.2

// package.json

 "dependencies": {
    "@astrojs/check": "^0.9.3",
    "@astrojs/tailwind": "^5.1.0",
    "@astrojs/vue": "^4.5.0",
    "astro": "^4.15.2",
    "tailwindcss": "^3.4.10",
    "typescript": "^5.5.4",
    "vue": "^3.5.1"
  },
  "devDependencies": {
    "@inlang/paraglide-astro": "^0.2.2",
    "@inlang/paraglide-js": "1.11.2",
    "@typescript-eslint/parser": "^8.4.0",
    "eslint": "^9.9.1",
    "eslint-plugin-astro": "^1.2.3",
    "eslint-plugin-jsx-a11y": "^6.10.0",
    "prettier": "^3.3.3",
    "prettier-plugin-astro": "^0.14.1"
  }

I don't have much knowledge about language servers, but in some other Vite/Vue projects I had to add some module declaration in env.d.ts, I wonder if that could also help in that case.

// env.d.ts

declare module "*.vue" {
  import { DefineComponent } from "vue";
  const component: DefineComponent<{}, {}, any>;
  export default component;
}
Hanawa02 commented 1 month ago

After a bit of an investigation I was able to find a "solution":

In my case I needed both class and id from HTML parameters as part of the Intrinsic Attributes.

[!WARNING] It did not work for me when adding it to env.d.ts

// src/astro-jsx.d.ts
declare namespace astroHTML.JSX {
  export interface IntrinsicAttributes {
    class?: string;
    id?: string;
  }
}