10

I have a Node project running an Express server that, when queried a news article with /amp in the URL, returns the AMP version of that article.

Due to the request of a client, these AMP articles are built using React components that are in the end rendered to static markup.

Here is one of the components:

import * as React from 'react';
import { Article_modulesByArticleId_edges_node } from '../../data/__generated__/Article';

// GraphQL auto generated type for the data that is sent to this component 
type Props = {
  module: Article_modulesByArticleId_edges_node;
};

export default (props: Props) => {
  const image = props.module.image;
  if (!image || !image.url || !image.title || !image.copyright) {
    return "";
  }

  return <amp-img alt={image.title} src={image.url} ... />
};

The problem?

The project also uses TypeScript, which I am not overly familiar with.

When I try to use a custom AMP element, TypeScript throws this error message:

[ts] Property 'amp-img' does not exist on type 'JSX.IntrinsicElements'.

I haven't been able to find any node packages with the AMP types defined, and overall not a lot of discussion about TypeScript and AMP.

Does anyone have any suggestions?

Do I have to write my own declarations for AMP elements?

nyedidikeke
  • 6,899
  • 7
  • 44
  • 59
tannerbaum
  • 394
  • 1
  • 3
  • 13
  • Here is a [github post](https://github.com/Microsoft/TypeScript/issues/15449) about the discussion on how to discuss a custom tags in the global. I think this solutions could help you on your problem. – MαπμQμαπkγVπ.0 May 30 '18 at 09:27

2 Answers2

26

So in the writing declarations of AMP elements front, it isn't so bad. There are three options:

// Any element you create will be accepted
declare namespace JSX {
 interface IntrinsicElements {
  [elemName: string]: any;
 }
}

// The elements you list here will be accepted, attributes don't matter
declare namespace JSX {
 interface IntrinsicElements {
  'amp-img': any;
 }
}

// The elements you list here will be accepted, and only with the attributes that you include here
declare namespace JSX {
 interface AmpImg {
  alt?: string;
  src?: string;
  width?: string;
  height?: string;
  layout?: string;
 }
 interface IntrinsicElements {
  'amp-img': AmpImg;
 }
}

The only downside with doing it manually like this is that you would have to update it yourself if the Amp spec changes. So let me know if there is currently another option.

Regardless, hope this helps someone!

tannerbaum
  • 394
  • 1
  • 3
  • 13
  • 1
    https://github.com/ampproject/amp-toolbox/tree/master/packages/validator-rules is an official source of the schema in machine readable format. – mjs Oct 03 '19 at 11:44
  • The first option is not working for me, and I think that is because naming an element has a higher specificity than `[element: string]`. – DedaDev Feb 09 '21 at 23:46
3

Inside a page, the answer given by @tannerbaum does not work for me as the namespace seems not used.

Instead, this did the trick for me:

declare global {
  namespace JSX {
    interface IntrinsicElements {
      [elemName: string]: any;
    }
  }
}
Sicaa
  • 153
  • 1
  • 8