简体   繁体   中英

TypeScript using Google Adsense with custom Interface

I followed a Tutorial to use Google Adsense in my Angular App. This is the out come:

in the index.html:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-1223823-NOT-MY-ACTUALLY-ID', 'auto');  // Change the UA-ID to the one you got from Google Analytics

</script>

And this is my app.component.ts:

import {Component} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';

// declare ga as a function to set and sent the events
declare let ga: Function;  // <-- Here is the "Error"

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'news-bootstrap';
  constructor(public router: Router) {
    // subscribe to router events and send page views to Google Analytics
    this.router.events.subscribe(event => {

      if (event instanceof NavigationEnd) {
        ga('set', 'page', event.urlAfterRedirects);
        ga('send', 'pageview');
      }
    });
  }
}

TsLint doesn't like the line 'declare let ga: Function': TSLint: Don't use 'Function' as a type. Avoid using the `Function` type. Prefer a specific function type, like `() => void`.(ban-types) TSLint: Don't use 'Function' as a type. Avoid using the `Function` type. Prefer a specific function type, like `() => void`.(ban-types)

How does this work with interfaces?

I need a interface with the methods setPage(urlAfterRedirects: string) and sendPageView() with the attributes ga that is linked via the index.html js script. Is this even 'the right way' to do it? What is the best way to 'Avoid using the Function type' in this case?

EDIT1: And can I shift the ga('create', 'UA-1223823-NOT-MY-ACTUALLY-ID', 'auto'); function to the interface, too?

Problem

When you declare that a variable has the type Function you are telling typescript "this is a function that any some amount of arguments which are any type and returns anything." There's quite a lot of unknowns there.

When you try to call that function, typescript can't check if you are calling it with the right arguments because it doesn't know what the arguments are supposed to be. What it's asking you for is a type that defines the specific signature of the function.

Simple Solution

For the ga function we can get a little bit more specific or a lot more specific. Here's an "a little bit more specific" that covers the two calls that you are making:

type GA = (action: string, field: string, value?: string) => void;

This says that our type GA is a function which takes two or three arguments which are all strings and returns nothing.

Complexities

In reality there's an alternative syntax for calling 'set' which is also valid but would not be supported by the above definition for type GA .

You can set one field and one value by passing the field and value as separate arguments to the function, which is what you're doing.

ga('set', 'page', '/about');

But you can also pass an object of fields and values:

ga('set', {
  page: '/about',
  title: 'About Us'
});

There are lots and lots of fields and not all of them are string , so the "a lot more specific" would be a type definition that makes sure that all your field names are valid and that all of the values match the specific field.

Fortunately there's an existing types package which has done that work already. It looks like they are declaring a global var ga (along with a few others), so you wouldn't need your declare let ga at all. (But I can't get this working in the TS Playground.)

Abstraction

You're also asking about creating an interface which acts as a façade between you and the underlying ga object, making it more intuitive to use. This is not a necessity but it can be useful. Here's an article I found about doing this for event tracking, where they use typescript to describe the specific events they want to track since ga allows pretty much anything.

Your interface would be this:

interface MyGaTracker {
    setPage(urlAfterRedirects: string): void;
    sendPageView(): void;
}

You could implement it using a class or a plain object:

export const Tracker: MyGaTracker = {
    setPage: (urlAfterRedirects: string): void => {
        ga('set', 'page', urlAfterRedirects);
    },
    sendPageView: (): void => {
        ga('send', 'pageview');
    }
}

Typescript Playground Link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM